From 916950353be5ff22db609a2d43748bc302e87a2a Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 29 Jan 2020 13:47:40 +0100 Subject: [PATCH 01/85] Code formatting --- .../home/shared/utils/CollapseAnimation.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/utils/CollapseAnimation.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/utils/CollapseAnimation.java index 55e9e88d82c..c232fb01223 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/utils/CollapseAnimation.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/utils/CollapseAnimation.java @@ -9,33 +9,33 @@ import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.Unit; public class CollapseAnimation extends Animation { - + private Element elementToAnimate; - + private boolean show = true; private int effectiveHeight; public CollapseAnimation(Element elementToAnimate) { this(elementToAnimate, true); } - + public CollapseAnimation(Element elementToAnimate, int effectiveHeight) { this(elementToAnimate, false); this.effectiveHeight = effectiveHeight; } - + public CollapseAnimation(Element elementToAnimate, boolean showInitial) { this.elementToAnimate = elementToAnimate; this.show = showInitial; elementToAnimate.getStyle().setOverflow(Overflow.HIDDEN); - if(!showInitial) { + if (!showInitial) { elementToAnimate.getStyle().setDisplay(Display.NONE); } } - + @Override protected void onComplete() { - if(!show) { + if (!show) { elementToAnimate.getStyle().setDisplay(Display.NONE); } else { elementToAnimate.getStyle().clearHeight(); @@ -44,14 +44,14 @@ public class CollapseAnimation extends Animation { @Override protected void onUpdate(double progress) { - if(!show) { + if (!show) { progress = 1.0 - progress; } elementToAnimate.getStyle().setHeight(effectiveHeight * progress, Unit.PX); } - + public void animate(boolean show) { - if(show && effectiveHeight == 0) { + if (show && effectiveHeight == 0) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { From 8499b27cc15af46f2fb23b57f207eb133bb13f59 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 29 Jan 2020 13:49:00 +0100 Subject: [PATCH 02/85] Removed unused fields --- .../sap/sailing/gwt/home/desktop/partials/stage/Stage.java | 3 --- .../com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java | 4 ---- 2 files changed, 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java index 5065ba9aa54..80a99ae5fb3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java @@ -24,14 +24,12 @@ public class Stage extends Composite { SimplePanel widgetCarouselContainerUi; private StageTeaser stageTeaser; - private List stageTeaserComposites; private final DesktopPlacesNavigator placeNavigator; public Stage(DesktopPlacesNavigator placeNavigator) { this.placeNavigator = placeNavigator; StageResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); - stageTeaserComposites = new ArrayList(); } public void setFeaturedEvents(List list) { @@ -50,7 +48,6 @@ public class Stage extends Composite { break; } widgetCarousel.addWidget(stageTeaser); - stageTeaserComposites.add(stageTeaser); } this.widgetCarouselContainerUi.setWidget(widgetCarousel); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java index 4e921c4df63..0763144111a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java @@ -21,8 +21,6 @@ public class Stage extends Composite { private static StageUiBinder uiBinder = GWT.create(StageUiBinder.class); - private List stageTeaserComposites; - @UiField DivElement stageContainer; @UiField WidgetCarousel widgetCarousel; @@ -36,7 +34,6 @@ public class Stage extends Composite { widgetCarousel.setShowArrows(false); if (!showDots) stageContainer.getStyle().setHeight(100, Unit.PCT); widgetCarousel.setShowDots(showDots); - stageTeaserComposites = new ArrayList(); } public void setFeaturedEvents(List list) { @@ -53,7 +50,6 @@ public class Stage extends Composite { break; } widgetCarousel.addWidget(stageTeaser); - stageTeaserComposites.add(stageTeaser); } } } From 5001a1492dcfb9daf89eb06dea785cc6da438c9c Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 29 Jan 2020 13:49:36 +0100 Subject: [PATCH 03/85] Removed unnecessary class cast --- .../sap/sse/gwt/client/controls/carousel/WidgetCarousel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index f705ea851e4..cd37bc8b6c5 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -84,7 +84,7 @@ public class WidgetCarousel extends Composite { public void onAfterChange() { for (Iterator iterator = items.iterator(); iterator.hasNext();) { - Widget widget = (Widget) iterator.next(); + Widget widget = iterator.next(); final Element parent = widget.getElement(); if (parent != null) { From 523f12180f99b075fcd60549d08ef63ae8970385 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 29 Jan 2020 13:51:26 +0100 Subject: [PATCH 04/85] Several improvements to ensure that slick slider is not initialized before the widget is attached to DOM and visible and the contents are set --- .../home/desktop/partials/stage/Stage.java | 8 ++-- .../gwt/home/mobile/partials/stage/Stage.java | 4 +- .../controls/carousel/WidgetCarousel.java | 46 +++++++++++++++---- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java index 80a99ae5fb3..1916b444844 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/stage/Stage.java @@ -33,8 +33,7 @@ public class Stage extends Composite { } public void setFeaturedEvents(List list) { - final WidgetCarousel widgetCarousel = new WidgetCarousel(); - widgetCarousel.setShowDots(false); + final List stageWidgets = new ArrayList<>(); for (EventStageDTO event : list) { switch (event.getStageType()) { case POPULAR: @@ -47,8 +46,11 @@ public class Stage extends Composite { stageTeaser = new UpcomingEventStageTeaser(event, placeNavigator); break; } - widgetCarousel.addWidget(stageTeaser); + stageWidgets.add(stageTeaser); } + final WidgetCarousel widgetCarousel = new WidgetCarousel(); + widgetCarousel.setShowDots(false); + widgetCarousel.setWidgets(stageWidgets); this.widgetCarouselContainerUi.setWidget(widgetCarousel); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java index 0763144111a..d279bcc1cf5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java @@ -37,6 +37,7 @@ public class Stage extends Composite { } public void setFeaturedEvents(List list) { + final List stageWidgets = new ArrayList<>(); for (EventLinkAndMetadataDTO event : list) { switch (event.getStageType()) { case POPULAR: @@ -49,7 +50,8 @@ public class Stage extends Composite { stageTeaser = new UpcomingEventStageTeaser(event, placeNavigator); break; } - widgetCarousel.addWidget(stageTeaser); + stageWidgets.add(stageTeaser); + widgetCarousel.setWidgets(stageWidgets); } } } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index cd37bc8b6c5..f2dc3f5b211 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -66,6 +66,8 @@ public class WidgetCarousel extends Composite { private boolean showArrows = true; private LinkedList items = new LinkedList(); + + private boolean initialized; /** * Widget constructor @@ -76,10 +78,25 @@ public class WidgetCarousel extends Composite { sliderMainUi.addStyleName(uniqueId); } + private void initializeIfRequired() { + // Slick slider may only get initialized when it is attached to DOM, visible and items are added + // Rendering bugs may occur otherwise + if (!initialized && !items.isEmpty() && isAttached() && isVisible()) { + init(); + initialized = true; + } + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + initializeIfRequired(); + } + @Override protected void onLoad() { super.onLoad(); - init(); + initializeIfRequired(); } public void onAfterChange() { @@ -116,7 +133,6 @@ public class WidgetCarousel extends Composite { * @param uniqueId */ native void setupSlider(WidgetCarousel sliderReference) /*-{ - $wnd .$( '.' @@ -160,16 +176,26 @@ public class WidgetCarousel extends Composite { * @param url * */ - public void addWidget(Widget slide) { - if (items.isEmpty()) { - if (slide instanceof LazyLoadable) { - LazyLoadable lazyLoadable = (LazyLoadable) slide; - lazyLoadable.doInitializeLazyComponents(); + public void setWidgets(Iterable slides) { + for (Widget slide : slides) { + items.add(slide); + sliderMainUi.add(slide); + } + if (!items.isEmpty()) { + ensureInitialized(items.getFirst()); + if (items.size() > 1) { + ensureInitialized(items.get(1)); } + ensureInitialized(items.getLast()); + } + initializeIfRequired(); + } + + private void ensureInitialized(Widget slide) { + if (slide instanceof LazyLoadable) { + LazyLoadable lazyLoadable = (LazyLoadable) slide; + lazyLoadable.doInitializeLazyComponents(); } - items.add(slide); - sliderMainUi.add(slide); - onAfterChange(); } /** From 2ff03a6d289915a2b481fbd6193ef98fa26c41e1 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Wed, 29 Jan 2020 13:56:29 +0100 Subject: [PATCH 05/85] Implemented lazy initialization for stages included in collapsed years in mobile event overview --- .../recents/EventsOverviewRecentYear.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java index 78c0a03d5ed..7d802044df2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java @@ -44,10 +44,15 @@ public class EventsOverviewRecentYear extends Composite { private boolean isContentVisible; + private boolean isStageInitialized; + private final CollapseAnimation animation; + + private final List events; public EventsOverviewRecentYear(EventListYearDTO yearDTO, MobilePlacesNavigator navigator, boolean showInitial) { - List events = yearDTO.getEvents(); + isContentVisible = showInitial; + events = yearDTO.getEvents(); eventStage = new Stage(navigator, false); EventsOverviewRecentResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); @@ -80,8 +85,7 @@ public class EventsOverviewRecentYear extends Composite { statisticsBox.getElement().getStyle().setPaddingRight(1, Unit.EM); recentEventsTeaserPanel.add(statisticsBox); - eventStage.setFeaturedEvents(events); -// eventStage.removeFromParent(); + initStageContents(); headerDiv.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { @@ -89,11 +93,17 @@ public class EventsOverviewRecentYear extends Composite { } }, ClickEvent.getType()); - isContentVisible = showInitial; animation = new CollapseAnimation(contentDiv, showInitial); updateAccordionState(); } + private void initStageContents() { + if (!isStageInitialized) { + eventStage.setFeaturedEvents(events); + isStageInitialized = true; + } + } + void onHeaderClicked() { isContentVisible = !isContentVisible; updateContentVisibility(); @@ -101,6 +111,7 @@ public class EventsOverviewRecentYear extends Composite { private void updateContentVisibility() { animation.animate(isContentVisible); + initStageContents(); updateAccordionState(); } From b26922bdb9a067abe2defb2ed3c31c26c3cd2f9c Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Thu, 30 Jan 2020 02:22:26 +0500 Subject: [PATCH 06/85] Custom TWA and TWS import parser and serialzer changes --- .../domain/common/orc/ORCCertificate.java | 3 + .../impl/ORCCertificateCustomBinsImpl.java | 51 ++++ .../common/orc/impl/ORCCertificateImpl.java | 10 + .../impl/ORCCertificatesCollectionJSON.java | 264 ++++++++++++------ .../impl/ORCCertificateJsonSerializer.java | 25 +- 5 files changed, 266 insertions(+), 87 deletions(-) create mode 100644 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java index 12c0f0925aa..f7582e5435d 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java @@ -128,4 +128,7 @@ public interface ORCCertificate extends WithID, Serializable { Map> getVelocityPredictionPerTrueWindSpeedAndAngle(); String getBoatName(); + + Speed[] getTrueWindSpeed(); + Bearing[] getTrueWindAngle(); } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java new file mode 100644 index 00000000000..7556a53763a --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java @@ -0,0 +1,51 @@ +package com.sap.sailing.domain.common.orc.impl; + +import java.util.Collection; +import java.util.Map; + +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; +import com.sap.sse.common.Duration; +import com.sap.sse.common.Speed; +import com.sap.sse.common.TimePoint; + +public class ORCCertificateCustomBinsImpl extends ORCCertificateImpl { + private static final long serialVersionUID = 6151992286427539248L; + + private final Speed[] customAllowancesTrueWindSpeeds; + private final Bearing[] customAllowancesTrueWindAngle; + + public ORCCertificateCustomBinsImpl(Collection customTrueWindSpeed, Collection customTrueWindAngle, + String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, + Distance length, Duration gph, Double cdl, TimePoint issueDate, + Map> velocityPredictionsPerTrueWindSpeedAndAngle, Map beatAngles, + Map beatVMGPredictionPerTrueWindSpeed, Map beatAllowancePerTrueWindSpeed, + Map runAngles, Map runVMGPredictionPerTrueWindSpeed, + Map runAllowancePerTrueWindSpeed, + Map windwardLeewardSpeedPredictionsPerTrueWindSpeed, + Map longDistanceSpeedPredictionsPerTrueWindSpeed, + Map circularRandomSpeedPredictionsPerTrueWindSpeed, + Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { + super(idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatClassName, length, gph, cdl, issueDate, + velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionsPerTrueWindSpeed, + longDistanceSpeedPredictionsPerTrueWindSpeed, circularRandomSpeedPredictionsPerTrueWindSpeed, + nonSpinnakerSpeedPredictionsPerTrueWindSpeed); + this.customAllowancesTrueWindAngle = new Bearing[customTrueWindAngle.size()]; + this.customAllowancesTrueWindSpeeds = new Speed[customTrueWindSpeed.size()]; + customTrueWindAngle.toArray(customAllowancesTrueWindAngle); + customTrueWindSpeed.toArray(customAllowancesTrueWindSpeeds); + } + + @Override + public Bearing[] getTrueWindAngle() { + return this.customAllowancesTrueWindAngle; + } + + @Override + public Speed[] getTrueWindSpeed() { + return this.customAllowancesTrueWindSpeeds; + } + +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java index 2c48fcd49fa..75110519648 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java @@ -255,4 +255,14 @@ public class ORCCertificateImpl implements ORCCertificate { return "Certificate with ID \""+getId()+"\" for "+getSailNumber()+" / "+getBoatName() + " - Issued on: " + (getIssueDate()==null?"n/a":getIssueDate().asDate())+" with GPH "+ Util.padPositiveValue(getGPHInSecondsToTheMile(), 1, 1, /* round */ true); } + + @Override + public Speed[] getTrueWindSpeed() { + return ALLOWANCES_TRUE_WIND_SPEEDS; + } + + @Override + public Bearing[] getTrueWindAngle() { + return ALLOWANCES_TRUE_WIND_ANGLES; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 6bb362d65ab..8f96f6b71f5 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -6,14 +6,19 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Predicate; +import java.util.TreeMap; +import java.util.regex.Pattern; import javax.xml.bind.DatatypeConverter; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.orc.ORCCertificate; +import com.sap.sailing.domain.common.orc.impl.ORCCertificateCustomBinsImpl; import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; @@ -79,17 +84,28 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec */ @Override public ORCCertificate getCertificateById(String certificateId) { - String boatName = null; + String boatName = null; String boatclass = null; - Distance length = null; - Duration gph = null; - Double cdl = null; + Distance length = null; + Duration gph = null; + Double cdl = null; TimePoint issueDate = null; Map beatAngles = new HashMap<>(); Map gybeAngles = new HashMap<>(); Map> velocityPredictionPerTrueWindSpeedAndAngle = new HashMap<>(); - Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); //per nautical mile - Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); //per nautical mile + Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); // per + // nautical + // mile + Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per + // nautical + // mile + boolean isNewFormatTWA = false; // determine whether use default TWS and TWA values or custom TWS and TWA values + Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the + // TrueWindSpeed + // So that it first sort the entries as well + // as no duplicates + Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap + Pattern p = Pattern.compile("^([R][0-9]{1,9}[.]{0,1}[0-9]{1})$"); // Pattern to recognize the new pattern JSONObject object = certificateJsonObjectsByCertificateId.get(certificateId); if (object == null) { // TODO Throw Exception for certificate by id not found. InvalidArgumentException? @@ -98,84 +114,155 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec String sailNumber = null; for (Entry entry : object.entrySet()) { switch ((String) entry.getKey()) { - case SAIL_NO: - sailNumber = entry.getValue() == null ? null : entry.getValue().toString(); - break; - case LOA: - length = new MeterDistance(((Number) entry.getValue()).doubleValue()); - break; - case YACHT_NAME: - boatName = entry.getValue()==null?null:entry.getValue().toString(); - break; - case CLASS: - boatclass = (String) entry.getValue(); - break; - case GPH2: - gph = new SecondsDurationImpl(((Number) entry.getValue()).doubleValue()); - break; - case CDL2: - cdl = ((Number) entry.getValue()).doubleValue(); - break; - case ISSUE_DATE: - Date date = DatatypeConverter.parseDateTime((String) entry.getValue()).getTime(); - issueDate = new MillisecondsTimePoint(date); - break; - case ALLOWANCES: - final JSONObject allowances = (JSONObject) object.get(ALLOWANCES); - for (final Object aKey : allowances.keySet()) { - final JSONArray array = (JSONArray) allowances.get(aKey); - if (((String) aKey).equals(BEAT_ANGLE)) { - for (int i = 0; i < array.size(); i++) { - beatAngles.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], - new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); - } - continue; + case SAIL_NO: + sailNumber = entry.getValue() == null ? null : entry.getValue().toString(); + break; + case LOA: + length = new MeterDistance(((Number) entry.getValue()).doubleValue()); + break; + case YACHT_NAME: + boatName = entry.getValue() == null ? null : entry.getValue().toString(); + break; + case CLASS: + boatclass = (String) entry.getValue(); + break; + case GPH2: + gph = new SecondsDurationImpl(((Number) entry.getValue()).doubleValue()); + break; + case CDL2: + cdl = ((Number) entry.getValue()).doubleValue(); + break; + case ISSUE_DATE: + Date date = DatatypeConverter.parseDateTime((String) entry.getValue()).getTime(); + issueDate = new MillisecondsTimePoint(date); + break; + case ALLOWANCES: + final JSONObject allowances = (JSONObject) object.get(ALLOWANCES); + + // Finding out the new format bins + Predicate isDefaultTrueWindAngle = (s) -> { + switch (s) { + case "R52": + case "R60": + case "R75": + case "R90": + case "R110": + case "R120": + case "R135": + case "R150": + return true; + default: + return false; + } + }; + // Before going further into parsing Allowances, First need to check whether + // The bins are with old format or New, If New Format is given then need to parse + // and calculate the bins accordingly + for (final Object aKey : allowances.keySet()) { + String keyString = (String) aKey; + boolean found = p.matcher(keyString).lookingAt(); + if (found && !isDefaultTrueWindAngle.test(keyString)) { + isNewFormatTWA = true; + Map twsMap = new HashMap<>(); // This map is use to save all the True wind + // speed and allowances per nautical miles + // against the True wind angle provide with new + // format. + final JSONArray array = (JSONArray) allowances.get(keyString); + for (int i = 0; i < array.size(); i++) { + JSONObject twa = (JSONObject) array.get(i); + Double twsValue = (Double) twa.get("twsInKts"); + trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue)); + twsMap.put(trueWindSpeedMap.get(twsValue), + new SecondsDurationImpl((Double) twa.get("allInSPNM"))); } - if (((String) aKey).equals(GYBE_ANGLE)) { - for (int i = 0; i < array.size(); i++) { - gybeAngles.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], - new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); - } - continue; + Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); + trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue)); + allowanceDurationsPerTrueWindAngleAndSpeed.put(trueWindAngleMap.get(trueWindAngleValue), + twsMap); + } + } + // Finding out the new bins end here + + final Double[] trueWindSpeedArray = new Double[trueWindSpeedMap.size()]; + trueWindSpeedMap.keySet().toArray(trueWindSpeedArray); + for (final Object aKey : allowances.keySet()) { + final JSONArray array = (JSONArray) allowances.get(aKey); + if (((String) aKey).equals(BEAT_ANGLE)) { + for (int i = 0; i < array.size(); i++) { + beatAngles.put( + isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) + : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); } - Map twsMap = new HashMap<>(); + continue; + } + if (((String) aKey).equals(GYBE_ANGLE)) { + for (int i = 0; i < array.size(); i++) { + gybeAngles.put( + isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) + : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); + } + continue; + } + Map twsMap = new HashMap<>(); + // As we don't want to calculate the allowancesDuarationPerTrueWindAngleAndSpeed for new format TWS + // and TWA + // So we need to ignore that parsing. If the TWS and TWA are with old format then this calculation + // occure's here + if (!isNewFormatTWA) { for (int i = 0; i < array.size(); i++) { twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } - switch ((String) aKey) { - case "R52": - case "R60": - case "R75": - case "R90": - case "R110": - case "R120": - case "R135": - case "R150": - allowanceDurationsPerTrueWindAngleAndSpeed.put( - new DegreeBearingImpl(Integer.parseInt(((String) aKey).substring(1))), twsMap); - break; - case BEAT: - case RUN: - predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); - break; - default: - predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); - break; - } } - break; - default: - break; + if (isNewFormatTWA && !p.matcher((String) aKey).lookingAt()) { + for (int i = 0; i < array.size(); i++) { + twsMap.put( + isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) + : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); + } + } + switch ((String) aKey) { + case "R52": + case "R60": + case "R75": + case "R90": + case "R110": + case "R120": + case "R135": + case "R150": + allowanceDurationsPerTrueWindAngleAndSpeed + .put(new DegreeBearingImpl(Integer.parseInt(((String) aKey).substring(1))), twsMap); + break; + case BEAT: + case RUN: + predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); + break; + default: + predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); + break; + } + } + break; + default: + break; + } + } + if (isNewFormatTWA) { + trueWindSpeedMap.keySet().forEach(tws -> { + velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); + }); + + } else { + for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + velocityPredictionPerTrueWindSpeedAndAngle.put(tws, new HashMap<>()); } - } - for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { - velocityPredictionPerTrueWindSpeedAndAngle.put(tws, new HashMap<>()); } for (Bearing keyTWA : allowanceDurationsPerTrueWindAngleAndSpeed.keySet()) { for (Speed keyTWS : allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).keySet()) { - velocityPredictionPerTrueWindSpeedAndAngle.get(keyTWS).put(keyTWA, - ORCCertificateImpl.NAUTICAL_MILE + velocityPredictionPerTrueWindSpeedAndAngle.get(keyTWS).put(keyTWA, ORCCertificateImpl.NAUTICAL_MILE .inTime(allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).get(keyTWS))); } } @@ -203,20 +290,35 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec nonSpinnakerSpeedPredictionPerTrueWindSpeed.put(tws, ORCCertificateImpl.NAUTICAL_MILE .inTime(predefinedAllowanceDurationsPerTrueWindSpeed.get(NON_SPINNAKER).get(tws))); } - return new ORCCertificateImpl(getId(object), sailNumber, boatName, boatclass, length, gph, cdl, - issueDate, velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, - longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, - nonSpinnakerSpeedPredictionPerTrueWindSpeed); + + // Need to populate the ORCCertificateImpl with custom TWA and TWS that's why there are two constructor's are + // being used. + if (isNewFormatTWA) { + + return new ORCCertificateCustomBinsImpl(trueWindSpeedMap.values(), trueWindAngleMap.values(), getId(object), + sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, + longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, + nonSpinnakerSpeedPredictionPerTrueWindSpeed); + } else { + return new ORCCertificateImpl(getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, + longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, + nonSpinnakerSpeedPredictionPerTrueWindSpeed); + } } - + + private String getId(JSONObject certificateAsJson) { return getIdFromFields(certificateAsJson.get(NAT_AUTH).toString(), certificateAsJson.get(CERT_NO) == null ? EMPTY_CERT_NO : certificateAsJson.get(CERT_NO).toString(), certificateAsJson.get(BIN).toString()); } - + private String getIdFromFields(final String natAuth, final String certNo, final String bin) { return natAuth + certNo + bin; } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index da7a665760e..c27d106e8de 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -28,6 +28,7 @@ public class ORCCertificateJsonSerializer implements JsonSerializer Date: Thu, 30 Jan 2020 09:39:45 +0100 Subject: [PATCH 07/85] Bug 5192: Fixed initialization of slick slider in case of back navigation to start page --- .../sap/sse/gwt/client/controls/carousel/WidgetCarousel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index f2dc3f5b211..bf147f8e58f 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -96,7 +96,7 @@ public class WidgetCarousel extends Composite { @Override protected void onLoad() { super.onLoad(); - initializeIfRequired(); + Scheduler.get().scheduleFinally(this::initializeIfRequired); } public void onAfterChange() { From c72ab38bf20da28690be3c01c2e553380cebfc32 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 30 Jan 2020 15:34:09 +0100 Subject: [PATCH 08/85] Bug 5192: Fixed double attaching of mobile StartView which breaks the slider --- .../gwt/home/mobile/places/start/StartActivity.java | 7 +++---- .../home/shared/refresh/AutoAttachingRefreshManager.java | 6 +++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/start/StartActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/start/StartActivity.java index eba4cd9d58a..65c2155157d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/start/StartActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/start/StartActivity.java @@ -15,7 +15,6 @@ import com.sap.sailing.gwt.home.mobile.places.start.StartView.Presenter; import com.sap.sailing.gwt.home.shared.app.ActivityCallback; import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; import com.sap.sailing.gwt.home.shared.places.start.StartPlace; -import com.sap.sailing.gwt.home.shared.refresh.RefreshManager; import com.sap.sailing.gwt.home.shared.refresh.RefreshManagerWithErrorAndBusy; import com.sap.sse.gwt.dispatch.shared.commands.ListResult; @@ -33,11 +32,13 @@ public class StartActivity extends AbstractActivity implements Presenter { panel.setWidget(clientFactory.createBusyView()); Window.setTitle(place.getTitle()); final StartView view = new StartViewImpl(StartActivity.this); + final RefreshManagerWithErrorAndBusy refreshManager = new RefreshManagerWithErrorAndBusy(view.asWidget(), panel, + clientFactory.getDispatch(), clientFactory); clientFactory.getDispatch().execute(new GetStagedEventsAction(true), new ActivityCallback>(clientFactory, panel) { @Override public void onSuccess(ListResult result) { - panel.setWidget(view.asWidget()); + refreshManager.attachContent(); Window.setTitle(place.getTitle()); view.setFeaturedEvents(result.getValues()); } @@ -50,8 +51,6 @@ public class StartActivity extends AbstractActivity implements Presenter { } }); - final RefreshManager refreshManager = new RefreshManagerWithErrorAndBusy(view.asWidget(), panel, - clientFactory.getDispatch(), clientFactory); refreshManager.add(view.getAnniversariesView(), new GetAnniversariesAction()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/refresh/AutoAttachingRefreshManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/refresh/AutoAttachingRefreshManager.java index 64d50bd6e39..bd517bc6603 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/refresh/AutoAttachingRefreshManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/refresh/AutoAttachingRefreshManager.java @@ -34,7 +34,11 @@ public class AutoAttachingRefreshManager extends LifecycleRefreshManager { @Override protected void onSuccessfulUpdate() { - if(container != null) { + attachContent(); + } + + public void attachContent() { + if (container != null) { LOG.log(Level.FINE, "Attaching refreshable content to container"); container.setWidget(content); container = null; From a4d196fb62f0dd6f20ddd656ebcfcedb4c0b3f0f Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 30 Jan 2020 15:36:22 +0100 Subject: [PATCH 09/85] Bug 5192: Fix to ensure that the contents are set once to the slider only --- .../com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java index d279bcc1cf5..cff987fc885 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/stage/Stage.java @@ -51,7 +51,7 @@ public class Stage extends Composite { break; } stageWidgets.add(stageTeaser); - widgetCarousel.setWidgets(stageWidgets); } + widgetCarousel.setWidgets(stageWidgets); } } From 1018eab2d4df52eebe4ed142a548ba0bc0ecb6a6 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 30 Jan 2020 15:38:02 +0100 Subject: [PATCH 10/85] Bug 5192: Initialization of the slick slider is deferred in case the size of its contents isn't known yet --- .../client/controls/carousel/WidgetCarousel.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index bf147f8e58f..1771537c61c 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -82,11 +82,22 @@ public class WidgetCarousel extends Composite { // Slick slider may only get initialized when it is attached to DOM, visible and items are added // Rendering bugs may occur otherwise if (!initialized && !items.isEmpty() && isAttached() && isVisible()) { - init(); initialized = true; + initAfterReflow(); } } + private void initAfterReflow() { + // Slick slider initialization will produce layout problems when the content widget sizes aren't calculated yet. + // Until the initial reflow happened, widget size are reported as 0. If that's the case, + // initialization is deferred after the reflow. + if (items.getFirst().getElement().getOffsetWidth() > 0) { + init(); + } else { + Scheduler.get().scheduleDeferred(this::initAfterReflow); + } + } + @Override public void setVisible(boolean visible) { super.setVisible(visible); @@ -96,6 +107,8 @@ public class WidgetCarousel extends Composite { @Override protected void onLoad() { super.onLoad(); + // onLoad is called before the element is completely attached to the DOM. + // Using scheduleFinally ensures, this is finished before continuing with the initialization. Scheduler.get().scheduleFinally(this::initializeIfRequired); } From d6e0c41ad7f0258b60ed85a5dac169e4a1e40879 Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Fri, 31 Jan 2020 17:15:49 +0500 Subject: [PATCH 11/85] changes in serializer and deserializer for dynamic bins in import certificates --- .../domain/common/orc/ORCCertificate.java | 4 +- .../impl/ORCCertificateCustomBinsImpl.java | 51 ------- .../common/orc/impl/ORCCertificateImpl.java | 48 +++++-- .../impl/ORCCertificatesCollectionJSON.java | 135 +++++++----------- ...CertificateImpl_CustomFieldSerializer.java | 36 ++++- .../impl/ORCCertificateJsonDeserializer.java | 42 +++++- .../impl/ORCCertificateJsonSerializer.java | 39 +++-- 7 files changed, 195 insertions(+), 160 deletions(-) delete mode 100644 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java index f7582e5435d..2ccafb5a5cc 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java @@ -129,6 +129,6 @@ public interface ORCCertificate extends WithID, Serializable { String getBoatName(); - Speed[] getTrueWindSpeed(); - Bearing[] getTrueWindAngle(); + Speed[] getTrueWindSpeeds(); + Bearing[] getTrueWindAngles(); } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java deleted file mode 100644 index 7556a53763a..00000000000 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateCustomBinsImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.sap.sailing.domain.common.orc.impl; - -import java.util.Collection; -import java.util.Map; - -import com.sap.sse.common.Bearing; -import com.sap.sse.common.Distance; -import com.sap.sse.common.Duration; -import com.sap.sse.common.Speed; -import com.sap.sse.common.TimePoint; - -public class ORCCertificateCustomBinsImpl extends ORCCertificateImpl { - private static final long serialVersionUID = 6151992286427539248L; - - private final Speed[] customAllowancesTrueWindSpeeds; - private final Bearing[] customAllowancesTrueWindAngle; - - public ORCCertificateCustomBinsImpl(Collection customTrueWindSpeed, Collection customTrueWindAngle, - String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, - Distance length, Duration gph, Double cdl, TimePoint issueDate, - Map> velocityPredictionsPerTrueWindSpeedAndAngle, Map beatAngles, - Map beatVMGPredictionPerTrueWindSpeed, Map beatAllowancePerTrueWindSpeed, - Map runAngles, Map runVMGPredictionPerTrueWindSpeed, - Map runAllowancePerTrueWindSpeed, - Map windwardLeewardSpeedPredictionsPerTrueWindSpeed, - Map longDistanceSpeedPredictionsPerTrueWindSpeed, - Map circularRandomSpeedPredictionsPerTrueWindSpeed, - Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { - super(idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatClassName, length, gph, cdl, issueDate, - velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionsPerTrueWindSpeed, - longDistanceSpeedPredictionsPerTrueWindSpeed, circularRandomSpeedPredictionsPerTrueWindSpeed, - nonSpinnakerSpeedPredictionsPerTrueWindSpeed); - this.customAllowancesTrueWindAngle = new Bearing[customTrueWindAngle.size()]; - this.customAllowancesTrueWindSpeeds = new Speed[customTrueWindSpeed.size()]; - customTrueWindAngle.toArray(customAllowancesTrueWindAngle); - customTrueWindSpeed.toArray(customAllowancesTrueWindSpeeds); - } - - @Override - public Bearing[] getTrueWindAngle() { - return this.customAllowancesTrueWindAngle; - } - - @Override - public Speed[] getTrueWindSpeed() { - return this.customAllowancesTrueWindSpeeds; - } - -} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java index 75110519648..98c51e85c92 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java @@ -110,20 +110,22 @@ public class ORCCertificateImpl implements ORCCertificate { private final Map circularRandomSpeedPredictionPerTrueWindSpeed; private final Map nonSpinnakerSpeedPredictionPerTrueWindSpeed; - - public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN, - String sailnumber, String boatName, String boatClassName, - Distance length, Duration gph, - Double cdl, TimePoint issueDate, - Map> velocityPredictionsPerTrueWindSpeedAndAngle, - Map beatAngles, Map beatVMGPredictionPerTrueWindSpeed, - Map beatAllowancePerTrueWindSpeed, Map runAngles, - Map runVMGPredictionPerTrueWindSpeed, + private final Speed[] allowancesTrueWindSpeeds; + private final Bearing[] allowancesTrueWindAngles; + + public ORCCertificateImpl(Speed[] dynamicTrueWindSpeed, Bearing[] dynamicTrueWindAngle, + String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, + Distance length, Duration gph, Double cdl, TimePoint issueDate, + Map> velocityPredictionsPerTrueWindSpeedAndAngle, Map beatAngles, + Map beatVMGPredictionPerTrueWindSpeed, Map beatAllowancePerTrueWindSpeed, + Map runAngles, Map runVMGPredictionPerTrueWindSpeed, Map runAllowancePerTrueWindSpeed, Map windwardLeewardSpeedPredictionsPerTrueWindSpeed, Map longDistanceSpeedPredictionsPerTrueWindSpeed, Map circularRandomSpeedPredictionsPerTrueWindSpeed, Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { + this.allowancesTrueWindAngles = dynamicTrueWindAngle; + this.allowancesTrueWindSpeeds = dynamicTrueWindSpeed; this.idConsistingOfNatAuthCertNoAndBIN = idConsistingOfNatAuthCertNoAndBIN; this.sailNumber = sailnumber; this.boatName = boatName; @@ -145,6 +147,26 @@ public class ORCCertificateImpl implements ORCCertificate { this.circularRandomSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(circularRandomSpeedPredictionsPerTrueWindSpeed); this.nonSpinnakerSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(nonSpinnakerSpeedPredictionsPerTrueWindSpeed); } + public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN, + String sailnumber, String boatName, String boatClassName, + Distance length, Duration gph, + Double cdl, TimePoint issueDate, + Map> velocityPredictionsPerTrueWindSpeedAndAngle, + Map beatAngles, Map beatVMGPredictionPerTrueWindSpeed, + Map beatAllowancePerTrueWindSpeed, Map runAngles, + Map runVMGPredictionPerTrueWindSpeed, + Map runAllowancePerTrueWindSpeed, + Map windwardLeewardSpeedPredictionsPerTrueWindSpeed, + Map longDistanceSpeedPredictionsPerTrueWindSpeed, + Map circularRandomSpeedPredictionsPerTrueWindSpeed, + Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { + this(ALLOWANCES_TRUE_WIND_SPEEDS,ALLOWANCES_TRUE_WIND_ANGLES,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatClassName, length, gph, cdl, issueDate, + velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionsPerTrueWindSpeed, + longDistanceSpeedPredictionsPerTrueWindSpeed, circularRandomSpeedPredictionsPerTrueWindSpeed, + nonSpinnakerSpeedPredictionsPerTrueWindSpeed); + } @Override public String getId() { @@ -257,12 +279,12 @@ public class ORCCertificateImpl implements ORCCertificate { } @Override - public Speed[] getTrueWindSpeed() { - return ALLOWANCES_TRUE_WIND_SPEEDS; + public Speed[] getTrueWindSpeeds() { + return allowancesTrueWindSpeeds; } @Override - public Bearing[] getTrueWindAngle() { - return ALLOWANCES_TRUE_WIND_ANGLES; + public Bearing[] getTrueWindAngles() { + return allowancesTrueWindAngles; } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 8f96f6b71f5..524f6fd4992 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -6,7 +6,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.function.Predicate; import java.util.TreeMap; import java.util.regex.Pattern; @@ -18,7 +17,6 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.orc.ORCCertificate; -import com.sap.sailing.domain.common.orc.impl.ORCCertificateCustomBinsImpl; import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; @@ -93,19 +91,17 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Map beatAngles = new HashMap<>(); Map gybeAngles = new HashMap<>(); Map> velocityPredictionPerTrueWindSpeedAndAngle = new HashMap<>(); - Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); // per - // nautical - // mile + Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); // per + // nautical + // mile Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per // nautical // mile boolean isNewFormatTWA = false; // determine whether use default TWS and TWA values or custom TWS and TWA values - Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the - // TrueWindSpeed - // So that it first sort the entries as well - // as no duplicates - Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap - Pattern p = Pattern.compile("^([R][0-9]{1,9}[.]{0,1}[0-9]{1})$"); // Pattern to recognize the new pattern + Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the TrueWindSpeed + // So that it first sort the entries as well as no duplicates + Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap + Pattern p = Pattern.compile("^([R][0-9]{1,9}[.]{0,1}[0-9]{1})$"); //Pattern to recognize the new pattern JSONObject object = certificateJsonObjectsByCertificateId.get(certificateId); if (object == null) { // TODO Throw Exception for certificate by id not found. InvalidArgumentException? @@ -138,89 +134,66 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec break; case ALLOWANCES: final JSONObject allowances = (JSONObject) object.get(ALLOWANCES); - - // Finding out the new format bins - Predicate isDefaultTrueWindAngle = (s) -> { - switch (s) { - case "R52": - case "R60": - case "R75": - case "R90": - case "R110": - case "R120": - case "R135": - case "R150": - return true; - default: - return false; - } - }; - // Before going further into parsing Allowances, First need to check whether - // The bins are with old format or New, If New Format is given then need to parse - // and calculate the bins accordingly + + // Before going further into parsing Allowances, First need to check whether + // The bins are with old format or New, If New Format is given then need to parse + // and calculate the bins accordingly for (final Object aKey : allowances.keySet()) { String keyString = (String) aKey; boolean found = p.matcher(keyString).lookingAt(); - if (found && !isDefaultTrueWindAngle.test(keyString)) { - isNewFormatTWA = true; - Map twsMap = new HashMap<>(); // This map is use to save all the True wind - // speed and allowances per nautical miles - // against the True wind angle provide with new - // format. + if (found) { final JSONArray array = (JSONArray) allowances.get(keyString); + if(array.size()<1 || ! (array.get(0) instanceof JSONObject)) { + break; + } + isNewFormatTWA = true; + Map twsMap = new HashMap<>(); // This map is use to save all the True wind speed and allowances per nautical miles against the True wind angle provide with new format. + for (int i = 0; i < array.size(); i++) { JSONObject twa = (JSONObject) array.get(i); Double twsValue = (Double) twa.get("twsInKts"); - trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue)); + trueWindSpeedMap.put(twsValue,new KnotSpeedImpl(twsValue)); twsMap.put(trueWindSpeedMap.get(twsValue), new SecondsDurationImpl((Double) twa.get("allInSPNM"))); } Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); - trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue)); - allowanceDurationsPerTrueWindAngleAndSpeed.put(trueWindAngleMap.get(trueWindAngleValue), - twsMap); + trueWindAngleMap.put(trueWindAngleValue,new DegreeBearingImpl(trueWindAngleValue)); + allowanceDurationsPerTrueWindAngleAndSpeed + .put(trueWindAngleMap.get(trueWindAngleValue), twsMap); } } - // Finding out the new bins end here - - final Double[] trueWindSpeedArray = new Double[trueWindSpeedMap.size()]; + //Finding out the new bins end here + + final Double [] trueWindSpeedArray = new Double[trueWindSpeedMap.size()]; trueWindSpeedMap.keySet().toArray(trueWindSpeedArray); for (final Object aKey : allowances.keySet()) { final JSONArray array = (JSONArray) allowances.get(aKey); if (((String) aKey).equals(BEAT_ANGLE)) { for (int i = 0; i < array.size(); i++) { - beatAngles.put( - isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) - : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + beatAngles.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); } continue; } if (((String) aKey).equals(GYBE_ANGLE)) { for (int i = 0; i < array.size(); i++) { - gybeAngles.put( - isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) - : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + gybeAngles.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); } continue; } Map twsMap = new HashMap<>(); - // As we don't want to calculate the allowancesDuarationPerTrueWindAngleAndSpeed for new format TWS - // and TWA - // So we need to ignore that parsing. If the TWS and TWA are with old format then this calculation - // occure's here - if (!isNewFormatTWA) { + // As we don't want to calculate the allowancesDuarationPerTrueWindAngleAndSpeed for new format TWS and TWA + // So we need to ignore that parsing. If the TWS and TWA are with old format then this calculation occure's here + if(!isNewFormatTWA){ for (int i = 0; i < array.size(); i++) { twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } - } - if (isNewFormatTWA && !p.matcher((String) aKey).lookingAt()) { + } + if(isNewFormatTWA && !p.matcher((String) aKey).lookingAt()) { for (int i = 0; i < array.size(); i++) { - twsMap.put( - isNewFormatTWA ? new KnotSpeedImpl(trueWindSpeedArray[i]) - : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + twsMap.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } } @@ -233,8 +206,8 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec case "R120": case "R135": case "R150": - allowanceDurationsPerTrueWindAngleAndSpeed - .put(new DegreeBearingImpl(Integer.parseInt(((String) aKey).substring(1))), twsMap); + allowanceDurationsPerTrueWindAngleAndSpeed + .put(new DegreeBearingImpl(Integer.parseInt(((String) aKey).substring(1))), twsMap); break; case BEAT: case RUN: @@ -250,12 +223,12 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec break; } } - if (isNewFormatTWA) { - trueWindSpeedMap.keySet().forEach(tws -> { + if(isNewFormatTWA) { + trueWindSpeedMap.keySet().forEach(tws->{ velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); }); - - } else { + + }else { for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { velocityPredictionPerTrueWindSpeedAndAngle.put(tws, new HashMap<>()); } @@ -290,25 +263,27 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec nonSpinnakerSpeedPredictionPerTrueWindSpeed.put(tws, ORCCertificateImpl.NAUTICAL_MILE .inTime(predefinedAllowanceDurationsPerTrueWindSpeed.get(NON_SPINNAKER).get(tws))); } - - // Need to populate the ORCCertificateImpl with custom TWA and TWS that's why there are two constructor's are - // being used. - if (isNewFormatTWA) { - - return new ORCCertificateCustomBinsImpl(trueWindSpeedMap.values(), trueWindAngleMap.values(), getId(object), - sailNumber, boatName, boatclass, length, gph, cdl, issueDate, - velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, - longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, - nonSpinnakerSpeedPredictionPerTrueWindSpeed); - } else { - return new ORCCertificateImpl(getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + + // Need to populate the ORCCertificateImpl with custom TWA and TWS that's why there are two constructor's are being used. + if(isNewFormatTWA) { + Bearing [] dynamicAllowancesTrueWindAngles = new Bearing[trueWindAngleMap.values().size()]; + Speed [] dynamicAllowancesTrueWindSpeeds = new Speed[trueWindSpeedMap.values().size()]; + trueWindAngleMap.values().toArray(dynamicAllowancesTrueWindAngles); + trueWindSpeedMap.values().toArray(dynamicAllowancesTrueWindSpeeds); + + return new ORCCertificateImpl(dynamicAllowancesTrueWindSpeeds,dynamicAllowancesTrueWindAngles,getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, nonSpinnakerSpeedPredictionPerTrueWindSpeed); + }else { + return new ORCCertificateImpl(getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, + longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, + nonSpinnakerSpeedPredictionPerTrueWindSpeed); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java index 96a1c9a3b32..678fd878486 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java @@ -1,5 +1,7 @@ package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.orc.impl; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -43,6 +45,8 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali streamWriter.writeObject(new HashMap<>(instance.getLongDistanceSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getCircularRandomSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getNonSpinnakerSpeedPredictions())); + streamWriter.writeObject(Arrays.asList(instance.getTrueWindAngles())); + streamWriter.writeObject(Arrays.asList(instance.getTrueWindSpeeds())); } @Override @@ -88,7 +92,11 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali final Map circularRandomSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); @SuppressWarnings("unchecked") final Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); - return new ORCCertificateImpl(idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatclass, length, gph, + + final Bearing[] trueWindAngles = getTrueWindAnglesFromStream(streamReader); + final Speed[] trueWindSpeeds = getTrueWindSpeedsFromStream(streamReader); + + return new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed, @@ -96,6 +104,32 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed); } + private static Speed[] getTrueWindSpeedsFromStream(SerializationStreamReader streamReader) { + Speed[] trueWindSpeeds = null; + try { + Object trueWindSpeedsObject = streamReader.readObject(); + if(trueWindSpeedsObject != null) { + trueWindSpeeds = (Speed[]) trueWindSpeedsObject; + } + } catch (SerializationException e) { + trueWindSpeeds = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS; + } + return trueWindSpeeds; + } + + private static Bearing[] getTrueWindAnglesFromStream(SerializationStreamReader streamReader) { + Bearing[] trueWindAngles = null; + try { + Object trueWindAnglesObject = streamReader.readObject(); + if(trueWindAnglesObject != null) { + trueWindAngles = (Bearing[]) trueWindAnglesObject; + } + } catch (SerializationException e) { + trueWindAngles = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES; + } + return trueWindAngles; + } + @Override public void deserializeInstance(SerializationStreamReader streamReader, ORCCertificateImpl instance) throws SerializationException { diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java index cc9f3c5ebc1..407158ec067 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java @@ -3,6 +3,7 @@ package com.sap.sailing.server.gateway.deserialization.racelog.impl; import java.util.HashMap; import java.util.Map; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; @@ -25,6 +26,7 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer longDistanceSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map circularRandomSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map nonSpinnakerSpeedPredictionPerTrueWindSpeed = new HashMap<>(); - for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + Speed[] trueWindSpeeds = convertJsonArrayToTrueWindSpeedOrReturnDefault(json); + Bearing[] trueWindAngles = convertJsonArrayToTrueWindAngleOrReturnDefault(json); + for (Speed tws : trueWindSpeeds) { String twsKey = ORCCertificateJsonSerializer.speedToKnotsString(tws); beatAngles.put(tws, new DegreeBearingImpl( ((Number) ((JSONObject) json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_BEAT_ANGLES)) @@ -86,7 +90,7 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer velocityPredictionAtCurrentTrueWindSpeedPerTrueWindAngle = new HashMap<>(); - for (Bearing twa : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES) { + for (Bearing twa : trueWindAngles) { String twaKey = ORCCertificateJsonSerializer.bearingToDegreeString(twa); velocityPredictionAtCurrentTrueWindSpeedPerTrueWindAngle.put(twa, new KnotSpeedImpl(((Number) ((JSONObject) ((JSONObject) json @@ -97,7 +101,7 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer Date: Fri, 31 Jan 2020 18:27:58 +0500 Subject: [PATCH 12/85] Serializing and Deserirializing of Bins is completed --- .../ORCCertificateImpl_CustomFieldSerializer.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java index 678fd878486..f9cd064bc8c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java @@ -3,6 +3,7 @@ package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.orc.im import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.google.gwt.user.client.rpc.CustomFieldSerializer; @@ -109,7 +110,10 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali try { Object trueWindSpeedsObject = streamReader.readObject(); if(trueWindSpeedsObject != null) { - trueWindSpeeds = (Speed[]) trueWindSpeedsObject; + @SuppressWarnings("unchecked") + List trueWIndSpeedList = (List) trueWindSpeedsObject; + trueWindSpeeds = new Speed[trueWIndSpeedList.size()]; + trueWIndSpeedList.toArray(trueWindSpeeds); } } catch (SerializationException e) { trueWindSpeeds = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS; @@ -122,7 +126,10 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali try { Object trueWindAnglesObject = streamReader.readObject(); if(trueWindAnglesObject != null) { - trueWindAngles = (Bearing[]) trueWindAnglesObject; + @SuppressWarnings("unchecked") + List trueWIndAnglesList = (List) trueWindAnglesObject; + trueWindAngles = new Bearing[trueWIndAnglesList.size()]; + trueWIndAnglesList.toArray(trueWindAngles); } } catch (SerializationException e) { trueWindAngles = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES; From 3d291c05800e12d38890d5d3928ba877d0f52d9c Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Fri, 31 Jan 2020 17:18:12 +0100 Subject: [PATCH 13/85] Bug 5192: Improvement to reduce flickering when slider is shown --- .../sap/sse/gwt/client/controls/carousel/WidgetCarousel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index 1771537c61c..56313024ab5 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -7,6 +7,7 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Command; @@ -76,6 +77,7 @@ public class WidgetCarousel extends Composite { initWidget(ourUiBinder.createAndBindUi(this)); uniqueId = Document.get().createUniqueId(); sliderMainUi.addStyleName(uniqueId); + sliderMainUi.getElement().getStyle().setOverflowY(Overflow.HIDDEN); } private void initializeIfRequired() { @@ -93,6 +95,7 @@ public class WidgetCarousel extends Composite { // initialization is deferred after the reflow. if (items.getFirst().getElement().getOffsetWidth() > 0) { init(); + Scheduler.get().scheduleDeferred(sliderMainUi.getElement().getStyle()::clearOverflowY); } else { Scheduler.get().scheduleDeferred(this::initAfterReflow); } From 4edeccb50f1ea9895b609432471f34ee1758941c Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Sat, 1 Feb 2020 21:34:08 +0500 Subject: [PATCH 14/85] Parsing both custom and static value of twa and tws --- .../impl/ORCCertificatesCollectionJSON.java | 71 +++++++------------ .../impl/ORCCertificateJsonSerializer.java | 8 +-- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 524f6fd4992..27c14781818 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -97,11 +97,11 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per // nautical // mile - boolean isNewFormatTWA = false; // determine whether use default TWS and TWA values or custom TWS and TWA values +// boolean isNewFormatTWA = false; // determine whether use default TWS and TWA values or custom TWS and TWA values Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the TrueWindSpeed // So that it first sort the entries as well as no duplicates Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap - Pattern p = Pattern.compile("^([R][0-9]{1,9}[.]{0,1}[0-9]{1})$"); //Pattern to recognize the new pattern + Pattern p = Pattern.compile("(R\\d+(\\.\\d+)?)"); //Pattern to recognize the new pattern JSONObject object = certificateJsonObjectsByCertificateId.get(certificateId); if (object == null) { // TODO Throw Exception for certificate by id not found. InvalidArgumentException? @@ -143,18 +143,24 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec boolean found = p.matcher(keyString).lookingAt(); if (found) { final JSONArray array = (JSONArray) allowances.get(keyString); - if(array.size()<1 || ! (array.get(0) instanceof JSONObject)) { + if(array.size()<1) { break; } - isNewFormatTWA = true; + Map twsMap = new HashMap<>(); // This map is use to save all the True wind speed and allowances per nautical miles against the True wind angle provide with new format. - for (int i = 0; i < array.size(); i++) { - JSONObject twa = (JSONObject) array.get(i); - Double twsValue = (Double) twa.get("twsInKts"); - trueWindSpeedMap.put(twsValue,new KnotSpeedImpl(twsValue)); - twsMap.put(trueWindSpeedMap.get(twsValue), - new SecondsDurationImpl((Double) twa.get("allInSPNM"))); + if((array.get(i) instanceof JSONObject)) { + JSONObject twa = (JSONObject) array.get(i); + Double twsValue = (Double) twa.get("twsInKts"); + trueWindSpeedMap.put(twsValue,new KnotSpeedImpl(twsValue)); + twsMap.put(trueWindSpeedMap.get(twsValue), + new SecondsDurationImpl((Double) twa.get("allInSPNM"))); + } else { + trueWindSpeedMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i].getKnots(),ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i]); + twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); + } + } Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); trueWindAngleMap.put(trueWindAngleValue,new DegreeBearingImpl(trueWindAngleValue)); @@ -170,14 +176,14 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec final JSONArray array = (JSONArray) allowances.get(aKey); if (((String) aKey).equals(BEAT_ANGLE)) { for (int i = 0; i < array.size(); i++) { - beatAngles.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + beatAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]), new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); } continue; } if (((String) aKey).equals(GYBE_ANGLE)) { for (int i = 0; i < array.size(); i++) { - gybeAngles.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + gybeAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]), new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); } continue; @@ -185,30 +191,14 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Map twsMap = new HashMap<>(); // As we don't want to calculate the allowancesDuarationPerTrueWindAngleAndSpeed for new format TWS and TWA // So we need to ignore that parsing. If the TWS and TWA are with old format then this calculation occure's here - if(!isNewFormatTWA){ + if(!p.matcher((String) aKey).lookingAt()) { for (int i = 0; i < array.size(); i++) { - twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], - new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); - } - } - if(isNewFormatTWA && !p.matcher((String) aKey).lookingAt()) { - for (int i = 0; i < array.size(); i++) { - twsMap.put(isNewFormatTWA?new KnotSpeedImpl(trueWindSpeedArray[i]):ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + twsMap.put(new KnotSpeedImpl(trueWindSpeedArray[i]), new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } } switch ((String) aKey) { - case "R52": - case "R60": - case "R75": - case "R90": - case "R110": - case "R120": - case "R135": - case "R150": - allowanceDurationsPerTrueWindAngleAndSpeed - .put(new DegreeBearingImpl(Integer.parseInt(((String) aKey).substring(1))), twsMap); - break; + case BEAT: case RUN: predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); @@ -223,16 +213,12 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec break; } } - if(isNewFormatTWA) { + trueWindSpeedMap.keySet().forEach(tws->{ velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); }); - }else { - for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { - velocityPredictionPerTrueWindSpeedAndAngle.put(tws, new HashMap<>()); - } - } + for (Bearing keyTWA : allowanceDurationsPerTrueWindAngleAndSpeed.keySet()) { for (Speed keyTWS : allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).keySet()) { velocityPredictionPerTrueWindSpeedAndAngle.get(keyTWS).put(keyTWA, ORCCertificateImpl.NAUTICAL_MILE @@ -265,7 +251,7 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec } // Need to populate the ORCCertificateImpl with custom TWA and TWS that's why there are two constructor's are being used. - if(isNewFormatTWA) { + Bearing [] dynamicAllowancesTrueWindAngles = new Bearing[trueWindAngleMap.values().size()]; Speed [] dynamicAllowancesTrueWindSpeeds = new Speed[trueWindSpeedMap.values().size()]; trueWindAngleMap.values().toArray(dynamicAllowancesTrueWindAngles); @@ -277,14 +263,7 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, nonSpinnakerSpeedPredictionPerTrueWindSpeed); - }else { - return new ORCCertificateImpl(getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, - velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, - longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, - nonSpinnakerSpeedPredictionPerTrueWindSpeed); - } + } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index 5f7df0308fd..10482059308 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -31,7 +31,7 @@ public class ORCCertificateJsonSerializer implements JsonSerializer Date: Sat, 1 Feb 2020 21:37:48 +0500 Subject: [PATCH 15/85] Formating the existing code --- .../impl/ORCCertificatesCollectionJSON.java | 69 ++++++++----------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 27c14781818..7e5d9fbb956 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -97,7 +97,6 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per // nautical // mile -// boolean isNewFormatTWA = false; // determine whether use default TWS and TWA values or custom TWS and TWA values Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the TrueWindSpeed // So that it first sort the entries as well as no duplicates Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap @@ -135,40 +134,37 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec case ALLOWANCES: final JSONObject allowances = (JSONObject) object.get(ALLOWANCES); - // Before going further into parsing Allowances, First need to check whether - // The bins are with old format or New, If New Format is given then need to parse - // and calculate the bins accordingly for (final Object aKey : allowances.keySet()) { String keyString = (String) aKey; boolean found = p.matcher(keyString).lookingAt(); if (found) { final JSONArray array = (JSONArray) allowances.get(keyString); - if(array.size()<1) { + if (array.size() < 1) { break; } - - Map twsMap = new HashMap<>(); // This map is use to save all the True wind speed and allowances per nautical miles against the True wind angle provide with new format. + + Map twsMap = new HashMap<>(); for (int i = 0; i < array.size(); i++) { - if((array.get(i) instanceof JSONObject)) { + if ((array.get(i) instanceof JSONObject)) { JSONObject twa = (JSONObject) array.get(i); Double twsValue = (Double) twa.get("twsInKts"); - trueWindSpeedMap.put(twsValue,new KnotSpeedImpl(twsValue)); + trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue)); twsMap.put(trueWindSpeedMap.get(twsValue), new SecondsDurationImpl((Double) twa.get("allInSPNM"))); } else { - trueWindSpeedMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i].getKnots(),ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i]); - twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], - new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); + trueWindSpeedMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i].getKnots(), + ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i]); + twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], + new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } - + } Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); - trueWindAngleMap.put(trueWindAngleValue,new DegreeBearingImpl(trueWindAngleValue)); - allowanceDurationsPerTrueWindAngleAndSpeed - .put(trueWindAngleMap.get(trueWindAngleValue), twsMap); + trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue)); + allowanceDurationsPerTrueWindAngleAndSpeed.put(trueWindAngleMap.get(trueWindAngleValue), + twsMap); } } - //Finding out the new bins end here final Double [] trueWindSpeedArray = new Double[trueWindSpeedMap.size()]; trueWindSpeedMap.keySet().toArray(trueWindSpeedArray); @@ -189,16 +185,14 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec continue; } Map twsMap = new HashMap<>(); - // As we don't want to calculate the allowancesDuarationPerTrueWindAngleAndSpeed for new format TWS and TWA - // So we need to ignore that parsing. If the TWS and TWA are with old format then this calculation occure's here - if(!p.matcher((String) aKey).lookingAt()) { + //ignore true wind angles key + if (!p.matcher((String) aKey).lookingAt()) { for (int i = 0; i < array.size(); i++) { twsMap.put(new KnotSpeedImpl(trueWindSpeedArray[i]), new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); } } switch ((String) aKey) { - case BEAT: case RUN: predefinedAllowanceDurationsPerTrueWindSpeed.put((String) aKey, twsMap); @@ -214,11 +208,10 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec } } - trueWindSpeedMap.keySet().forEach(tws->{ - velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); - }); + trueWindSpeedMap.keySet().forEach(tws -> { + velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); + }); - for (Bearing keyTWA : allowanceDurationsPerTrueWindAngleAndSpeed.keySet()) { for (Speed keyTWS : allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).keySet()) { velocityPredictionPerTrueWindSpeedAndAngle.get(keyTWS).put(keyTWA, ORCCertificateImpl.NAUTICAL_MILE @@ -249,20 +242,18 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec nonSpinnakerSpeedPredictionPerTrueWindSpeed.put(tws, ORCCertificateImpl.NAUTICAL_MILE .inTime(predefinedAllowanceDurationsPerTrueWindSpeed.get(NON_SPINNAKER).get(tws))); } - - // Need to populate the ORCCertificateImpl with custom TWA and TWS that's why there are two constructor's are being used. - - Bearing [] dynamicAllowancesTrueWindAngles = new Bearing[trueWindAngleMap.values().size()]; - Speed [] dynamicAllowancesTrueWindSpeeds = new Speed[trueWindSpeedMap.values().size()]; - trueWindAngleMap.values().toArray(dynamicAllowancesTrueWindAngles); - trueWindSpeedMap.values().toArray(dynamicAllowancesTrueWindSpeeds); - - return new ORCCertificateImpl(dynamicAllowancesTrueWindSpeeds,dynamicAllowancesTrueWindAngles,getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, - velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, - longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, - nonSpinnakerSpeedPredictionPerTrueWindSpeed); + Bearing[] dynamicAllowancesTrueWindAngles = new Bearing[trueWindAngleMap.values().size()]; + Speed[] dynamicAllowancesTrueWindSpeeds = new Speed[trueWindSpeedMap.values().size()]; + trueWindAngleMap.values().toArray(dynamicAllowancesTrueWindAngles); + trueWindSpeedMap.values().toArray(dynamicAllowancesTrueWindSpeeds); + + return new ORCCertificateImpl(dynamicAllowancesTrueWindSpeeds, dynamicAllowancesTrueWindAngles, getId(object), + sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, + beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, + runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, + longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, + nonSpinnakerSpeedPredictionPerTrueWindSpeed); } From 602566eef87f19b0de52175da442110339492ee9 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 3 Feb 2020 10:35:00 +0100 Subject: [PATCH 16/85] Bug 5192: Additional changes to reduce flickering when slider is shown --- .../sse/gwt/client/controls/carousel/WidgetCarousel.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index 56313024ab5..291909e21bf 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -8,6 +8,7 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Command; @@ -93,8 +94,11 @@ public class WidgetCarousel extends Composite { // Slick slider initialization will produce layout problems when the content widget sizes aren't calculated yet. // Until the initial reflow happened, widget size are reported as 0. If that's the case, // initialization is deferred after the reflow. - if (items.getFirst().getElement().getOffsetWidth() > 0) { + if (items.getFirst().getElement().getOffsetWidth() > 10 + && items.getFirst().getElement().getOffsetHeight() > 10) { init(); + sliderMainUi.getElement().getStyle().setWidth(items.getFirst().getElement().getOffsetWidth(), Unit.PX); + sliderMainUi.getElement().getStyle().setHeight(items.getFirst().getElement().getOffsetHeight(), Unit.PX); Scheduler.get().scheduleDeferred(sliderMainUi.getElement().getStyle()::clearOverflowY); } else { Scheduler.get().scheduleDeferred(this::initAfterReflow); From 3d27a1872a342e0b7ddfdb52cff0ba875cbc8ecf Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 3 Feb 2020 10:38:34 +0100 Subject: [PATCH 17/85] Bug 5192: Additional change I forgot to include in the last commit --- .../com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index 291909e21bf..e6d0458ac76 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -78,6 +78,7 @@ public class WidgetCarousel extends Composite { initWidget(ourUiBinder.createAndBindUi(this)); uniqueId = Document.get().createUniqueId(); sliderMainUi.addStyleName(uniqueId); + sliderMainUi.getElement().getStyle().setHeight(0, Unit.PX); sliderMainUi.getElement().getStyle().setOverflowY(Overflow.HIDDEN); } From a86072faf616293a70765bdc7f5d9089ba3831b8 Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Mon, 3 Feb 2020 19:42:29 +0500 Subject: [PATCH 18/85] replacing dots with P for twa and tws key for mongo document --- .../racelog/impl/ORCCertificateJsonSerializer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index 10482059308..ae3f36a15ea 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -146,7 +146,9 @@ public class ORCCertificateJsonSerializer implements JsonSerializer Date: Mon, 3 Feb 2020 18:47:06 +0100 Subject: [PATCH 19/85] bug5193: adjusting server group in UserStore after receiving initial load --- .../igtimiadapter/impl/IgtimiWindTracker.java | 2 +- .../gwt/ui/server/SailingServiceImpl.java | 6 +- .../test/MediaReplicationTest.java | 2 +- .../server/test/MasterDataImportTest.java | 2 +- .../server/test/SearchServiceTest.java | 2 +- .../sap/sailing/server/impl/Activator.java | 2 +- .../impl/ReplicationServiceImpl.java | 1 + .../sse/security/shared/BasicUserStore.java | 17 +++++- .../sse/security/interfaces/UserStore.java | 11 +++- ...licationLeadingToEmailReplicationTest.java | 2 +- .../jaxrs/api/SecurityResourceTest.java | 2 +- .../security/test/AccessControlStoreTest.java | 6 +- .../com/sap/sse/security/test/LoginTest.java | 2 +- .../security/test/PermissionCheckerTest.java | 2 +- .../test/UserStoreWithPersistenceTest.java | 2 +- .../SecurityServiceMockFactory.java | 2 +- .../sse/security/ui/client/UserService.java | 2 +- .../mongodb/DomainObjectFactory.java | 2 +- .../userstore/mongodb/UserStoreImpl.java | 60 ++++++++++--------- .../userstore/mongodb/impl/Activator.java | 4 +- .../com/sap/sse/security/SecurityService.java | 14 ++++- .../com/sap/sse/security/impl/Activator.java | 2 +- .../security/impl/SecurityServiceImpl.java | 36 +++++++---- 23 files changed, 114 insertions(+), 69 deletions(-) diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiWindTracker.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiWindTracker.java index 11ab02afd69..75e8acf9f6c 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiWindTracker.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiWindTracker.java @@ -101,7 +101,7 @@ public class IgtimiWindTracker extends AbstractWindTracker implements WindTracke final UserGroup groupOwner = ownershipOfRace == null ? null : ownershipOfRace.getAnnotation().getTenantOwner(); if (groupOwner != null) { - if (groupOwner.equals(optionalSecurityService.getDefaultTenant())) { + if (groupOwner.equals(optionalSecurityService.getServerGroup())) { // It is assumed to be an auto-migration case isPermittedToUseAccount = true; } else { 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 ef144ee9f76..f8dac4955a2 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 @@ -5215,7 +5215,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet @Override public ServerConfigurationDTO getServerConfiguration() { SailingServerConfiguration sailingServerConfiguration = getService().getSailingServerConfiguration(); - UserGroup serverTenant = getSecurityService().getDefaultTenant(); + UserGroup serverTenant = getSecurityService().getServerGroup(); StrippedUserGroupDTO serverTenantDTO = new SecurityDTOFactory() .createStrippedUserGroupDTOFromUserGroup(serverTenant, new HashMap<>()); ServerConfigurationDTO result = new ServerConfigurationDTO(sailingServerConfiguration.isStandaloneServer(), @@ -5244,7 +5244,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet if (serverConfiguration.isPublic() != null) { final RoleDefinition viewerRole = getSecurityService() .getRoleDefinition(SailingViewerRole.getInstance().getId()); - final UserGroup defaultServerTenant = getSecurityService().getDefaultTenant(); + final UserGroup defaultServerTenant = getSecurityService().getServerGroup(); if (viewerRole != null && defaultServerTenant != null) { final boolean isCurrentlyPublic = Boolean.TRUE.equals(defaultServerTenant.getRoleAssociation(viewerRole)); final boolean shouldBePublic = serverConfiguration.isPublic(); @@ -9317,7 +9317,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet final Boolean result; final RoleDefinition viewerRole = getSecurityService() .getRoleDefinition(SailingViewerRole.getInstance().getId()); - final UserGroup defaultServerTenant = getSecurityService().getDefaultTenant(); + final UserGroup defaultServerTenant = getSecurityService().getServerGroup(); if (viewerRole != null && defaultServerTenant != null) { result = defaultServerTenant.getRoleAssociation(viewerRole); } else { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java index 48152e18b35..e7d6ea8bd00 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java @@ -198,7 +198,7 @@ public class MediaReplicationTest extends AbstractServerReplicationTest { User currentUser = new UserImpl("test", "email@test", Collections.emptyMap(), null); SecurityService securityService = Mockito.mock(SecurityService.class); - Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant(); + Mockito.doReturn(defaultTenant).when(securityService).getServerGroup(); Mockito.doReturn(currentUser).when(securityService).getCurrentUser(); Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any()); Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any()); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index d2d9bd26556..676843e4443 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -201,7 +201,7 @@ public class MasterDataImportTest { Subject fakeSubject = Mockito.mock(Subject.class); SecurityUtils.setSecurityManager(securityManager); Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any()); - Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant(); + Mockito.doReturn(defaultTenant).when(securityService).getServerGroup(); Mockito.doReturn(currentUser).when(securityService).getCurrentUser(); Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any()); Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any()); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java index c909089aff6..83d03a59039 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java @@ -126,7 +126,7 @@ public class SearchServiceTest { SecurityUtils.setSecurityManager(securityManager); Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any()); - Mockito.doReturn(defaultTenant).when(securityService).getDefaultTenant(); + Mockito.doReturn(defaultTenant).when(securityService).getServerGroup(); Mockito.doReturn(currentUser).when(securityService).getCurrentUser(); Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any()); Mockito.doNothing().when(securityService).checkCurrentUserReadPermission(Mockito.any()); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index 79cac631b26..2327fdd041c 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -216,7 +216,7 @@ public class Activator implements BundleActivator { if (securityService.isInitialOrMigration()) { // The server is initially set to be public by adding sailing_viewer role to the server group // with forAll=true - securityService.putRoleDefinitionToUserGroup(securityService.getDefaultTenant(), + securityService.putRoleDefinitionToUserGroup(securityService.getServerGroup(), sailingViewerRoleDefinition, true); // sailing_viewer role is publicly readable securityService.addToAccessControlList(sailingViewerRoleDefinition.getIdentifier(), null, DefaultActions.READ.name()); diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index 585115770ac..93727d82335 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -679,6 +679,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM * the replica is registered for these {@link Replicable}s. The master sends operations only for * replicables that at least one replica has registered for. This may mean that operations are received * for replicables for which no replicable was requested. Replicas shall drop such operations silently. + * TODO unused... * * @return the UUID that the master generated for this client which is also entered into {@link #replicaUUIDs} */ diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java index c980cecb31b..55b2d3d681c 100755 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/BasicUserStore.java @@ -8,6 +8,7 @@ import com.sap.sse.common.Named; import com.sap.sse.common.Util.Pair; import com.sap.sse.security.shared.impl.Ownership; import com.sap.sse.security.shared.impl.Role; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; import com.sap.sse.security.shared.impl.User; import com.sap.sse.security.shared.impl.UserGroup; import com.sap.sse.security.shared.impl.UserGroupImpl; @@ -153,9 +154,21 @@ public interface BasicUserStore extends UserGroupProvider, Named { * {@link UserGroupImpl} object whose name equals that of the default tenant name. It will have been used * during role migration where string-based roles are mapped to a corresponding {@link RoleDefinition} * and the users with the original role will obtain a corresponding {@link Role} with this default - * tenant as the {@link Role#getQualifiedForTenant() tenant qualifier}. + * tenant as the {@link Role#getQualifiedForTenant() tenant qualifier}. It is by default used as the + * group owner of the {@link SecuredSecurityTypes#SERVER} object for the local server/replica set. */ - UserGroup getDefaultTenant(); + UserGroup getServerGroup(); + + /** + * For use after replica initialization / initial load, when the server group has been resolved against + * the initial load received from the master instance. + */ + void setServerGroup(UserGroup newServerGroup); + + /** + * The name of the {@link #getServerGroup() server group}. + */ + String getServerGroupName(); /** * Ensures that the predefined role definitions, particularly the "admin" and the "user" role, exist. diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java index 42c65675b91..ae6246ec2b7 100644 --- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java +++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java @@ -18,10 +18,10 @@ import com.sap.sse.security.shared.impl.UserGroupImpl; */ public interface UserStore extends BasicUserStore { /** - * An instance of the bundle hosting this service may have a default tenant. If so, the default tenant's name is + * An instance of the bundle hosting this service may have a server group. If so, the default server group's name is * read from a system property whose name is provided by this constant. */ - String DEFAULT_TENANT_NAME_PROPERTY_NAME = "security.defaultTenantName"; + String DEFAULT_SERVER_GROUP_NAME_PROPERTY_NAME = "security.defaultServerGroupName"; String ADMIN_USERNAME = "admin"; /** @@ -94,7 +94,12 @@ public interface UserStore extends BasicUserStore { */ void loadAndMigrateUsers() throws UserGroupManagementException, UserManagementException; - void ensureDefaultTenantExists() throws UserGroupManagementException; + /** + * Looks up a {@link UserGroup} based on the server group name set for this user store. If no such group + * can be found, one is created and returned. If a group by that name exists, it will be set as this {@link UserStore}'s + * {@link #getServerGroup() server group} and will be returned. + */ + UserGroup ensureServerGroupExists() throws UserGroupManagementException; void removeAllQualifiedRolesForUser(User user); diff --git a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java index 1ca6483afe8..4b75e60e482 100644 --- a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java +++ b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java @@ -73,7 +73,7 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe final AccessControlStore accessControlStore = new AccessControlStoreImpl(userStore); SecurityServiceImpl result = new SecurityServiceImpl(trackerMock, userStore, accessControlStore); userStore.ensureDefaultRolesExist(); - userStore.ensureDefaultTenantExists(); + userStore.ensureServerGroupExists(); result.initialize(); result.clearReplicaState(); return result; diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/jaxrs/api/SecurityResourceTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/jaxrs/api/SecurityResourceTest.java index ccf7a79e732..706bebe68d4 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/jaxrs/api/SecurityResourceTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/jaxrs/api/SecurityResourceTest.java @@ -59,7 +59,7 @@ public class SecurityResourceTest { try { store = new UserStoreImpl("TestDefaultTenant"); store.ensureDefaultRolesExist(); - store.ensureDefaultTenantExists(); + store.ensureServerGroupExists(); accessControlStore = new AccessControlStoreImpl(store); Activator.setTestStores(store, accessControlStore); service = new SecurityServiceImpl(/* mailServiceTracker */ null, diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/AccessControlStoreTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/AccessControlStoreTest.java index 087b3bceae9..f28c0df0325 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/AccessControlStoreTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/AccessControlStoreTest.java @@ -73,7 +73,7 @@ public class AccessControlStoreTest { userStore = new UserStoreImpl(DEFAULT_TENANT_NAME); userStore.loadAndMigrateUsers(); userStore.ensureDefaultRolesExist(); - userStore.ensureDefaultTenantExists(); + userStore.ensureServerGroupExists(); } catch (UserGroupManagementException | UserManagementException e) { throw new RuntimeException(e); } @@ -101,13 +101,13 @@ public class AccessControlStoreTest { @Test public void testCreateSimpleAccessControlListForGroup() throws UserGroupManagementException { - final UserGroup group = userStore.getDefaultTenant(); + final UserGroup group = userStore.getServerGroup(); accessControlStore.addAclPermission(testId, group, "READ"); assertNotNull(accessControlStore.getAccessControlList(testId)); assertTrue(accessControlStore.getAccessControlList(testId).getAnnotation().getActionsByUserGroup().get(group).contains("READ")); newStores(); assertNotNull(accessControlStore.getAccessControlList(testId)); - final UserGroup newGroup = userStore.getDefaultTenant(); + final UserGroup newGroup = userStore.getServerGroup(); assertTrue(accessControlStore.getAccessControlList(testId).getAnnotation().getActionsByUserGroup().get(newGroup).contains("READ")); } 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 e6a8b019499..ba5d985825a 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 @@ -57,7 +57,7 @@ public class LoginTest { db.getCollection(com.sap.sse.security.persistence.impl.CollectionNames.SESSIONS.name()).drop(); userStore = new UserStoreImpl(DEFAULT_TENANT_NAME); userStore.ensureDefaultRolesExist(); - userStore.ensureDefaultTenantExists(); + userStore.ensureServerGroupExists(); accessControlStore = new AccessControlStoreImpl(userStore); Activator.setTestStores(userStore, accessControlStore); Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); // to enable shiro to find classes from com.sap.sse.security diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java index 25d771d1c10..1743e7a9ad4 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java @@ -73,7 +73,7 @@ public class PermissionCheckerTest { final String adminTenantName = "admin-tenant"; userStore = new UserStoreImpl(adminTenantName); userStore.ensureDefaultRolesExist(); - userStore.ensureDefaultTenantExists(); + userStore.ensureServerGroupExists(); accessControlStore = new AccessControlStoreImpl(userStore); AbstractCompositeAuthorizingRealm.setTestStores(userStore, accessControlStore); realm = new UsernamePasswordRealm(); diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreWithPersistenceTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreWithPersistenceTest.java index cc29ec00619..1ea34f792fa 100644 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreWithPersistenceTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/UserStoreWithPersistenceTest.java @@ -82,7 +82,7 @@ public class UserStoreWithPersistenceTest { try { store = new UserStoreImpl("TestDefaultTenant"); store.ensureDefaultRolesExist(); - store.ensureDefaultTenantExists(); + store.ensureServerGroupExists(); store.loadAndMigrateUsers(); } catch (UserGroupManagementException | UserManagementException e) { throw new RuntimeException(e); diff --git a/java/com.sap.sse.security.testsupport/src/com/sap/sse/security/testsupport/SecurityServiceMockFactory.java b/java/com.sap.sse.security.testsupport/src/com/sap/sse/security/testsupport/SecurityServiceMockFactory.java index 26baa2e1c14..3bff429806c 100755 --- a/java/com.sap.sse.security.testsupport/src/com/sap/sse/security/testsupport/SecurityServiceMockFactory.java +++ b/java/com.sap.sse.security.testsupport/src/com/sap/sse/security/testsupport/SecurityServiceMockFactory.java @@ -17,7 +17,7 @@ public class SecurityServiceMockFactory { public static SecurityService mockSecurityService() { UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant"); final SecurityService result = Mockito.mock(SecurityService.class); - Mockito.doReturn(defaultTenant).when(result).getDefaultTenant(); + Mockito.doReturn(defaultTenant).when(result).getServerGroup(); Mockito.doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java index b260272f43d..553b41ee4c0 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java @@ -471,7 +471,7 @@ public class UserService { /** * Checks whether the user has permission to {@link DefaultActions#CREATE create} an object of the logical type * specified, assuming that it will be created with this user as the {@link Ownership#getUserOwner() user owner} and - * this user's {@link #getDefaultTenant() default group} as the {@link Ownership#getTenantOwner() group owner}. + * this user's {@link #getServerGroup() default group} as the {@link Ownership#getTenantOwner() group owner}. */ public boolean hasCreatePermission(HasPermissions logicalSecuredObjectType) { if (currentUser == null) { diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/DomainObjectFactory.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/DomainObjectFactory.java index ab56e967a67..6057c59bf6b 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/DomainObjectFactory.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/DomainObjectFactory.java @@ -47,7 +47,7 @@ public interface DomainObjectFactory { * @param userGroupProvider * a way for the user object that will be created to dynamically obtain the user groups to which it * belongs - * @return the user objects returned have dummy objects for their {@link SecurityUser#getDefaultTenant() default + * @return the user objects returned have dummy objects for their {@link SecurityUser#getServerGroup() default * tenant} and for their {@link SecurityUser#getRoles() roles} attribute which need to be replaced by the * caller once the {@link Tenant} objects and all user objects have been loaded from the DB. The only field * that is set correctly in those dummy {@link Tenant} objects is their {@link Tenant#getId() ID} field. The diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java index 58d819aeb4a..0c5eaf7d96e 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java @@ -63,7 +63,7 @@ public class UserStoreImpl implements UserStore { private static final String ACCESS_TOKEN_KEY = "___access_token___"; - private String name = "MongoDB user store"; + private final static String NAME = "MongoDB user store"; /** * If a valid default tenant name was passed to the constructor, this field will contain a valid @@ -72,7 +72,7 @@ public class UserStoreImpl implements UserStore { * the original role will obtain a corresponding {@link Role} with this default tenant as the * {@link Role#getQualifiedForTenant() tenant qualifier}. */ - private UserGroup defaultTenant; + private UserGroup serverGroup; private final ConcurrentHashMap userGroups; private final ConcurrentHashMap userGroupsByName; @@ -137,16 +137,16 @@ public class UserStoreImpl implements UserStore { */ private final transient DomainObjectFactory domainObjectFactory; - private final String defaultTenantName; + private final String serverGroupName; - public UserStoreImpl(String defaultTenantName) throws UserGroupManagementException, UserManagementException { + public UserStoreImpl(String defaultServerGroupName) throws UserGroupManagementException, UserManagementException { this(PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(), - PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), defaultTenantName); + PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), defaultServerGroupName); } public UserStoreImpl(final DomainObjectFactory domainObjectFactory, final MongoObjectFactory mongoObjectFactory, - String defaultTenantName) throws UserGroupManagementException, UserManagementException { - this.defaultTenantName = defaultTenantName; + String defaultServerGroupName) throws UserGroupManagementException, UserManagementException { + this.serverGroupName = defaultServerGroupName; this.domainObjectFactory = domainObjectFactory; roleDefinitions = new ConcurrentHashMap<>(); userGroups = new ConcurrentHashMap<>(); @@ -191,14 +191,10 @@ public class UserStoreImpl implements UserStore { } } - /** - * Do only call for testcases, to ensure the Server is correctly setup, without loading all users - * - * @throws UserGroupManagementException - */ @Override - public void ensureDefaultTenantExists() throws UserGroupManagementException { - defaultTenant = getOrCreateDefaultTenant(defaultTenantName); + public UserGroup ensureServerGroupExists() throws UserGroupManagementException { + serverGroup = getOrCreateServerGroup(serverGroupName); + return serverGroup; } /** @@ -212,7 +208,7 @@ public class UserStoreImpl implements UserStore { userGroupsByName.put(group.getName(), group); } // do this here, in case the default tenant was just loaded before - ensureDefaultTenantExists(); + ensureServerGroupExists(); for (User u : domainObjectFactory.loadAllUsers(roleDefinitions, this::convertToNewRoleModel, this.userGroups, this)) { users.put(u.getName(), u); addToUsersByEmail(u); @@ -254,12 +250,12 @@ public class UserStoreImpl implements UserStore { // Special of the global admin's admin role to ensure that one initial user has global admin permissions groupQualifierForMigratedRole = null; } else { - if (defaultTenant == null) { + if (serverGroup == null) { throw new IllegalStateException( - "For role migration a valid default tenant is required. Set system property " - + UserStore.DEFAULT_TENANT_NAME_PROPERTY_NAME + " or provide a server name"); + "For role migration a valid server group name is required. Set system property " + + UserStore.DEFAULT_SERVER_GROUP_NAME_PROPERTY_NAME + " or provide a server name"); } - groupQualifierForMigratedRole = defaultTenant; + groupQualifierForMigratedRole = serverGroup; } result = new Role(roleDefinition, groupQualifierForMigratedRole, /* user qualification */ null); break; @@ -297,17 +293,27 @@ public class UserStoreImpl implements UserStore { } @Override - public UserGroup getDefaultTenant() { - return defaultTenant; + public String getServerGroupName() { + return serverGroupName; } - private UserGroup getOrCreateDefaultTenant(String defaultTenantName) throws UserGroupManagementException { + @Override + public UserGroup getServerGroup() { + return serverGroup; + } + + @Override + public void setServerGroup(UserGroup newServerGroup) { + this.serverGroup = newServerGroup; + } + + private UserGroup getOrCreateServerGroup(String defaultServerGroupName) throws UserGroupManagementException { final UserGroup result; - if (defaultTenantName != null) { - final UserGroup existingTenant = getUserGroupByName(defaultTenantName); + if (defaultServerGroupName != null) { + final UserGroup existingTenant = getUserGroupByName(defaultServerGroupName); if (existingTenant == null) { - logger.info("Couldn't find default tenant " + defaultTenantName + "; creating it"); - result = createUserGroup(UUID.randomUUID(), defaultTenantName); + logger.info("Couldn't find default tenant " + defaultServerGroupName + "; creating it"); + result = createUserGroup(UUID.randomUUID(), defaultServerGroupName); } else { result = existingTenant; } @@ -559,7 +565,7 @@ public class UserStoreImpl implements UserStore { @Override public String getName() { - return name; + return NAME; } @Override diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/Activator.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/Activator.java index 89089c9ef4b..bdd259f559a 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/Activator.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/impl/Activator.java @@ -33,9 +33,9 @@ public class Activator implements BundleActivator { public void start(BundleContext bundleContext) throws Exception { Activator.context = bundleContext; logger.info("Creating user store"); - final String defaultTenantName = System.getProperty(UserStore.DEFAULT_TENANT_NAME_PROPERTY_NAME, + final String defaultServerGroupName = System.getProperty(UserStore.DEFAULT_SERVER_GROUP_NAME_PROPERTY_NAME, ServerInfo.getName() + "-server"); - final UserStoreImpl userStore = new UserStoreImpl(defaultTenantName); + final UserStoreImpl userStore = new UserStoreImpl(defaultServerGroupName); AccessControlStoreImpl accessControlStore = new AccessControlStoreImpl(userStore); accessControlStoreRegistration = context.registerService(AccessControlStore.class.getName(), accessControlStore, null); 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 c2ed710f782..2a3ea204a7a 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 @@ -356,9 +356,19 @@ public interface SecurityService extends ReplicableWithObjectInputStream + * + * During replication, a replica's user store contents are replaced by the master's user store contents. However, + * a replica's {@link UserStore#getServerGroup()} will be resolved by the server group name provided to the + * {@link UserStore} at its construction time. If such a group is not provided by the master, it is created. This + * creation will be executed as a replicable operation that hence will be sent back to the master where the group + * is then known. This new group is then used to try to set the server's group ownership, after checking that such an + * ownership doesn't exist yet. */ - UserGroup getDefaultTenant(); + UserGroup getServerGroup(); /** * For the current session's {@link Subject} an ownership for an object of type {@code type} and with type-relative 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 49622cf30f4..1c46ea379ad 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 @@ -161,7 +161,7 @@ public class Activator implements BundleActivator { userStore.clear(); accessControlStore.clear(); userStore.ensureDefaultRolesExist(); - userStore.ensureDefaultTenantExists(); + userStore.ensureServerGroupExists(); getSecurityService().initialize(); applyCustomizations(); } 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 8475b5d630c..133954f0ac1 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 @@ -325,11 +325,11 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat ADMIN_DEFAULT_PASSWORD, /* fullName */ null, /* company */ null, Locale.ENGLISH, /* validationBaseURL */ null, null); - setOwnership(adminUser.getIdentifier(), adminUser, null); Role adminRole = new Role(adminRoleDefinition); addRoleForUserAndSetUserAsOwner(adminUser, adminRole); - final UserGroup defaultTenant = getDefaultTenant(); + // add new admin user to server group and make server group the default creation group for the admin user: + final UserGroup defaultTenant = getServerGroup(); addUserToUserGroup(defaultTenant, adminUser); setDefaultTenantForCurrentServerForUser(adminUser.getName(), defaultTenant.getId()); } @@ -340,7 +340,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat User allUser = apply(s->s.internalCreateUser(SecurityService.ALL_USERNAME, null)); // user is explicitly not owned by itself because this would enable anybody to modify this user - setOwnership(allUser.getIdentifier(), null, getDefaultTenant()); + setOwnership(allUser.getIdentifier(), null, getServerGroup()); // The permission to create new users is initially added but not recreated on server start if the admin removed it in the meanwhile. // This allows servers to be configured to not permit self-registration of new users but only users being managed by an admin user. @@ -350,7 +350,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat .getQualifiedObjectIdentifier(PermissionAndRoleAssociation.get(createUserPermission, allUser)); // Permission association is owned by the server tenant. // This typically ensures that the server admin is able to remove the association. - setOwnership(qualifiedTypeIdentifierForPermission, null, getDefaultTenant()); + setOwnership(qualifiedTypeIdentifierForPermission, null, getServerGroup()); } if (isInitialOrMigration) { @@ -1658,8 +1658,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat } @Override - public UserGroup getDefaultTenant() { - return store.getDefaultTenant(); + public UserGroup getServerGroup() { + return store.getServerGroup(); } @Override @@ -2044,7 +2044,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat if (potentiallyExistingRoleDefinition == null) { result = store.createRoleDefinition(rolePrototype.getId(), rolePrototype.getName(), rolePrototype.getPermissions()); - setOwnership(result.getIdentifier(), null, getDefaultTenant()); + setOwnership(result.getIdentifier(), null, getServerGroup()); } else { result = potentiallyExistingRoleDefinition; } @@ -2082,8 +2082,18 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat } logger.info("Reading user store..."); try { - UserStore newUserStore = (UserStore) is.readObject(); + final UserStore newUserStore = (UserStore) is.readObject(); + final UserGroup newServerGroup = newUserStore.getUserGroupByName(store.getServerGroupName()); + // before replacing the local UserStore's contents, capture the server group: + final UserGroup oldServerGroup = store.getServerGroup(); store.replaceContentsFrom(newUserStore); + if (newServerGroup == null) { + // create the server group in a replication-aware fashion, making sure it appears on the master + apply(s->s.internalCreateUserGroup(UUID.randomUUID(), store.getServerGroupName())); + store.setServerGroup(store.getUserGroupByName(store.getServerGroupName())); + } else if (newServerGroup != oldServerGroup) { + store.setServerGroup(newServerGroup); + } } finally { Thread.currentThread().setContextClassLoader(oldCCL); } @@ -2185,17 +2195,17 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat final RoleDefinition adminRoleDefinition = getRoleDefinition(AdminRole.getInstance().getId()); for (Role roleOfUser : user.getRoles()) { if (roleOfUser.getRoleDefinition().equals(adminRoleDefinition)) { - final UserGroup defaultTenant = getDefaultTenant(); + final UserGroup serverGroup = getServerGroup(); if (roleOfUser.getQualifiedForTenant() == null - || roleOfUser.getQualifiedForTenant().equals(defaultTenant)) { + || roleOfUser.getQualifiedForTenant().equals(serverGroup)) { // The user is a server admin -> Add it to the server group to allow setting the server // group as default creation group - addUserToUserGroup(defaultTenant, user); + addUserToUserGroup(serverGroup, user); if (UserStore.ADMIN_USERNAME.equals(user.getName())) { // For the "admin" user the server group is initially set as // default creation group. This is consistent to a newly created server // and in most cases this is the group, the admin is meant to work with. - setDefaultTenantForCurrentServerForUser(user.getName(), getDefaultTenant().getId()); + setDefaultTenantForCurrentServerForUser(user.getName(), getServerGroup().getId()); } } } @@ -2230,7 +2240,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat // initialize ownerships on migration and fix objects that were orphaned by e.g. deleting the owning user/group if (owner == null || owner.getAnnotation().getTenantOwner() == null && owner.getAnnotation().getUserOwner() == null) { - final UserGroup tenantOwnerToSet = setServerGroupAsOwner ? this.getDefaultTenant() : null; + final UserGroup tenantOwnerToSet = setServerGroupAsOwner ? this.getServerGroup() : null; logger.info("missing Ownership fixed: Setting ownership for: " + identifier + " to tenant: " + tenantOwnerToSet + "; user: " + userOwnerToSet); From 9ab246433c08b01d43b6945045de436b329ce238 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 10:06:05 +0100 Subject: [PATCH 20/85] fixed logging --- .../src/com/sap/sse/security/impl/SecurityServiceImpl.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 133954f0ac1..94e83de6874 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 @@ -333,15 +333,12 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat addUserToUserGroup(defaultTenant, adminUser); setDefaultTenantForCurrentServerForUser(adminUser.getName(), defaultTenant.getId()); } - if (store.getUserByName(SecurityService.ALL_USERNAME) == null) { isInitialOrMigration = true; logger.info(SecurityService.ALL_USERNAME + " not found -> creating it now"); User allUser = apply(s->s.internalCreateUser(SecurityService.ALL_USERNAME, null)); - // user is explicitly not owned by itself because this would enable anybody to modify this user setOwnership(allUser.getIdentifier(), null, getServerGroup()); - // The permission to create new users is initially added but not recreated on server start if the admin removed it in the meanwhile. // This allows servers to be configured to not permit self-registration of new users but only users being managed by an admin user. WildcardPermission createUserPermission = SecuredSecurityTypes.USER.getPermission(DefaultActions.CREATE); @@ -352,7 +349,6 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat // This typically ensures that the server admin is able to remove the association. setOwnership(qualifiedTypeIdentifierForPermission, null, getServerGroup()); } - if (isInitialOrMigration) { // predefined roles are meant to be publicly readable for (UUID predefinedRoleId : getPredefinedRoleIds()) { @@ -726,7 +722,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat @Override public void putRoleDefinitionToUserGroup(UserGroup userGroup, RoleDefinition roleDefinition, boolean forAll) { - logger.info("Removing role definition " + roleDefinition.getName() + "(forAll = " + forAll + ") to group " + logger.info("Adding role definition " + roleDefinition.getName() + "(forAll = " + forAll + ") to group " + userGroup.getName()); apply(s -> s.internalPutRoleDefinitionToUserGroup(userGroup.getId(), roleDefinition.getId(), forAll)); } From 117155394b82a456471103bc2d6531dc9f32ee7e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 10:06:24 +0100 Subject: [PATCH 21/85] rename defaultServerTenant to serverGroup --- .../sailing/gwt/ui/server/SailingServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 f8dac4955a2..56ce0a4465f 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 @@ -5244,19 +5244,19 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet if (serverConfiguration.isPublic() != null) { final RoleDefinition viewerRole = getSecurityService() .getRoleDefinition(SailingViewerRole.getInstance().getId()); - final UserGroup defaultServerTenant = getSecurityService().getServerGroup(); - if (viewerRole != null && defaultServerTenant != null) { - final boolean isCurrentlyPublic = Boolean.TRUE.equals(defaultServerTenant.getRoleAssociation(viewerRole)); + final UserGroup serverGroup = getSecurityService().getServerGroup(); + if (viewerRole != null && serverGroup != null) { + final boolean isCurrentlyPublic = Boolean.TRUE.equals(serverGroup.getRoleAssociation(viewerRole)); final boolean shouldBePublic = serverConfiguration.isPublic(); if (isCurrentlyPublic != shouldBePublic) { // value changed - if (getSecurityService().hasCurrentUserUpdatePermission(defaultServerTenant) + if (getSecurityService().hasCurrentUserUpdatePermission(serverGroup) && getSecurityService().hasCurrentUserMetaPermissionsOfRoleDefinitionWithQualification( - viewerRole, new Ownership(null, defaultServerTenant))) { + viewerRole, new Ownership(null, serverGroup))) { if (serverConfiguration.isPublic()) { - getSecurityService().putRoleDefinitionToUserGroup(defaultServerTenant, viewerRole, true); + getSecurityService().putRoleDefinitionToUserGroup(serverGroup, viewerRole, true); } else { - getSecurityService().removeRoleDefintionFromUserGroup(defaultServerTenant, viewerRole); + getSecurityService().removeRoleDefintionFromUserGroup(serverGroup, viewerRole); } } else { throw new AuthorizationException("No permission to make the server public"); From db615cc6ef67b32f51d61c01c7818242ea20d157 Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Tue, 4 Feb 2020 15:06:21 +0500 Subject: [PATCH 22/85] adding junit test cases for serialization and deserialization of orc certificate --- .../resources/rms/newFormatCertificate.json | 394 ++++++++++++++++++ .../resources/rms/oldFormatCertificate.json | 215 ++++++++++ .../ORCCertificateJsonDeserializerTest.java | 59 +++ .../ORCCertificateJsonSerializerTest.java | 48 +++ 4 files changed, 716 insertions(+) create mode 100644 java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json create mode 100644 java/com.sap.sailing.server.gateway.serialization.test/resources/rms/oldFormatCertificate.json create mode 100644 java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java create mode 100644 java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java diff --git a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json new file mode 100644 index 00000000000..c1fe9e96e27 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json @@ -0,0 +1,394 @@ +{ + "rms":[ + + { + "NatAuth":"GRE", + "BIN":"GR4988", + "CertNo":"000905", + "RefNo":"GRE01015120", + "SailNo":"GRE-4988", + "YachtName":"GLAFKI II2", + "Class":"ONE-OFF", + "Builder":"MIKE PARRY", + "Designer":"ROB HUMPHREYS", + "Address3":"IO CHANION", + "C_Type":"CLUB", + "Division":"R", + "IssueDate":"2019-03-09T22:09:46.000Z", + "Age_Year":1981, + "CrewWT":520, + "LOA":9.225, + "IMSL":7.118, + "Draft":1.784, + "MB":3.158, + "Dspl_Measurement":2427, + "Stability_Index":112.6, + "Dynamic_Allowance":0, + "GPH":657.9, + "TMF_Inshore":0.9475, + "ILCWA":712.4, + "TMF_Offshore":0.9351, + "OSN":641.6, + "CDL":7.481, + "TN_Offshore_Low":0.9136, + "TN_Offshore_Medium":1.1487, + "TN_Offshore_High":1.2752, + "TN_Inshore_Low":0.7117, + "TN_Inshore_Medium":0.946, + "TN_Inshore_High":1.0685, + "TND_Offshore_Low":738.8, + "TND_Offshore_Medium":587.6, + "TND_Offshore_High":529.3, + "TND_Inshore_Low":948.4, + "TND_Inshore_Medium":713.5, + "TND_Inshore_High":631.7, + "Double_Handed_TOD":null, + "Double_Handed_TOT":null, + "OSN_Jibs":661, + "TMF_Jibs":0.9077, + "Dspl_Sailing":2955, + "WSS":15.63, + "Area_Main":30.18, + "Area_Jib":23.35, + "Area_Sym":49.95, + "Area_ASym":49.37, + "GPH_Jibs":679.9, + "GPH_Jibs_TOT":0.8825, + "GPH_DH":0, + "GPH_DH_TOT":0, + "Allowances":{ + "Beat":[ + 1036.9, + 864.7, + 780.4, + 754, + 743.7, + 739.4, + 737.3 + ], + "Run":[ + 1049.1, + 843, + 709.9, + 625.2, + 569.2, + 535.4, + 481.8 + ], + "BeatAngle":[ + 40.8, + 40.8, + 38.2, + 37.2, + 36.8, + 36.8, + 37.5 + ], + "GybeAngle":[ + 147.5, + 151, + 153.5, + 168.9, + 180, + 180, + 180 + ], + "WL":[ + 1043, + 853.8, + 745.2, + 689.6, + 656.5, + 637.4, + 609.5 + ], + "CR":[ + 890.9, + 726.2, + 639.4, + 589.6, + 559.1, + 538.7, + 511.2 + ], + "OC":[ + 1043.6, + 805.8, + 678.6, + 609.8, + 572.4, + 541.2, + 486.8 + ], + "NS":[ + 929.4, + 753.6, + 660.1, + 606.1, + 572.9, + 551, + 522.5 + ], + "OL":[ + 991.7, + 813.8, + 724.1, + 679.6, + 654.6, + 638.3, + 611.9 + ], + "R52.1":[ + { + "twsInKts":7.5, + "allInSPNM":561.1 + }, + { + "twsInKts":8.5, + "allInSPNM":562.1 + }, + { + "twsInKts":9.5, + "allInSPNM":563.1 + }, + { + "twsInKts":12.5, + "allInSPNM":564.1 + }, + { + "twsInKts":16.5, + "allInSPNM":565.1 + }, + { + "twsInKts":20.5, + "allInSPNM":566.1 + }, + { + "twsInKts":30.5, + "allInSPNM":567.1 + } + + ], + "R60.1":[ + { + "twsInKts":7.5, + "allInSPNM":511.1 + }, + { + "twsInKts":8.5, + "allInSPNM":512.1 + }, + { + "twsInKts":9.5, + "allInSPNM":513.1 + }, + { + "twsInKts":12.5, + "allInSPNM":514.1 + }, + { + "twsInKts":16.5, + "allInSPNM":515.1 + }, + { + "twsInKts":20.5, + "allInSPNM":516.1 + }, + { + "twsInKts":30.5, + "allInSPNM":517.1 + } + + ], + "R75.1":[ + { + "twsInKts":7.5, + "allInSPNM":521.1 + }, + { + "twsInKts":8.5, + "allInSPNM":522.1 + }, + { + "twsInKts":9.5, + "allInSPNM":523.1 + }, + { + "twsInKts":12.5, + "allInSPNM":524.1 + }, + { + "twsInKts":16.5, + "allInSPNM":525.1 + }, + { + "twsInKts":20.5, + "allInSPNM":526.1 + }, + { + "twsInKts":30.5, + "allInSPNM":527.1 + } + + ], + "R150.1":[ + { + "twsInKts":7.5, + "allInSPNM":531.1 + }, + { + "twsInKts":8.5, + "allInSPNM":532.1 + }, + { + "twsInKts":9.5, + "allInSPNM":533.1 + }, + { + "twsInKts":12.5, + "allInSPNM":534.1 + }, + { + "twsInKts":16.5, + "allInSPNM":535.1 + }, + { + "twsInKts":20.5, + "allInSPNM":536.1 + }, + { + "twsInKts":30.5, + "allInSPNM":537.1 + } + + ], + "R90.1":[ + { + "twsInKts":7.5, + "allInSPNM":541.1 + }, + { + "twsInKts":8.5, + "allInSPNM":542.1 + }, + { + "twsInKts":9.5, + "allInSPNM":543.1 + }, + { + "twsInKts":12.5, + "allInSPNM":544.1 + }, + { + "twsInKts":16.5, + "allInSPNM":545.1 + }, + { + "twsInKts":20.5, + "allInSPNM":546.1 + }, + { + "twsInKts":30.5, + "allInSPNM":547.1 + } + + ], + "R110.1":[ + { + "twsInKts":7.5, + "allInSPNM":551.1 + }, + { + "twsInKts":8.5, + "allInSPNM":552.1 + }, + { + "twsInKts":9.5, + "allInSPNM":553.1 + }, + { + "twsInKts":12.5, + "allInSPNM":554.1 + }, + { + "twsInKts":16.5, + "allInSPNM":555.1 + }, + { + "twsInKts":20.5, + "allInSPNM":556.1 + }, + { + "twsInKts":30.5, + "allInSPNM":557.1 + } + + ], + "R120.1":[ + { + "twsInKts":7.5, + "allInSPNM":571.1 + }, + { + "twsInKts":8.5, + "allInSPNM":572.1 + }, + { + "twsInKts":9.5, + "allInSPNM":573.1 + }, + { + "twsInKts":12.5, + "allInSPNM":574.1 + }, + { + "twsInKts":16.5, + "allInSPNM":575.1 + }, + { + "twsInKts":20.5, + "allInSPNM":576.1 + }, + { + "twsInKts":30.5, + "allInSPNM":577.1 + } + + ], + "R135.1":[ + { + "twsInKts":7.5, + "allInSPNM":581.1 + }, + { + "twsInKts":8.5, + "allInSPNM":582.1 + }, + { + "twsInKts":9.5, + "allInSPNM":583.1 + }, + { + "twsInKts":12.5, + "allInSPNM":584.1 + }, + { + "twsInKts":16.5, + "allInSPNM":585.1 + }, + { + "twsInKts":20.5, + "allInSPNM":586.1 + }, + { + "twsInKts":30.5, + "allInSPNM":587.1 + } + + ] + + } + } + + ] +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/oldFormatCertificate.json b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/oldFormatCertificate.json new file mode 100644 index 00000000000..53459f41269 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/oldFormatCertificate.json @@ -0,0 +1,215 @@ +{ + "rms": [ + { + "NatAuth": "GRE", + "BIN": "GR49735", + "CertNo": "000021", + "RefNo": "GRE01015829", + "SailNo": "GRE-49735", + "YachtName": "NEMO", + "Class": "PHANTOM 34", + "Builder": "Genzel", + "Designer": "Streuer", + "Address3": "NO RAHON", + "C_Type": "CLUB", + "Division": "C", + "IssueDate": "2019-06-11T21:37:02.000Z", + "Age_Year": 1988, + "CrewWT": 400, + "LOA": 10.3, + "IMSL": 8.518, + "Draft": 1.777, + "MB": 3.15, + "Dspl_Measurement": 6386, + "Stability_Index": 131.7, + "Dynamic_Allowance": 0.702, + "GPH": 780.1, + "TMF_Inshore": 0.8017, + "ILCWA": 842, + "TMF_Offshore": 0.798, + "OSN": 751.9, + "CDL": 7.056, + "TN_Offshore_Low": 0.7209, + "TN_Offshore_Medium": 0.9908, + "TN_Offshore_High": 1.1542, + "TN_Inshore_Low": 0.5529, + "TN_Inshore_Medium": 0.7913, + "TN_Inshore_High": 0.9571, + "TND_Offshore_Low": 936.3, + "TND_Offshore_Medium": 681.3, + "TND_Offshore_High": 584.8, + "TND_Inshore_Low": 1220.8, + "TND_Inshore_Medium": 853, + "TND_Inshore_High": 705.3, + "Double_Handed_TOD": null, + "Double_Handed_TOT": null, + "OSN_Jibs": 751.9, + "TMF_Jibs": 0.798, + "Dspl_Sailing": 7116, + "WSS": 27.39, + "Area_Main": 24.97, + "Area_Jib": 35.81, + "Area_Sym": 0, + "Area_ASym": 0, + "GPH_Jibs": 780.1, + "GPH_Jibs_TOT": 0.7691, + "GPH_DH": 0, + "GPH_DH_TOT": 0, + "Allowances": { + "Beat": [ + 1363.4, + 1113.9, + 981.7, + 892.9, + 841.8, + 822.5, + 818.8 + ], + "Run": [ + 1368.9, + 1036.8, + 849.8, + 734.8, + 653.5, + 592.3, + 531 + ], + "BeatAngle": [ + 45.9, + 43.6, + 42.7, + 43.1, + 43.2, + 42.8, + 43 + ], + "GybeAngle": [ + 167.1, + 167.8, + 168.9, + 170, + 170.1, + 171.4, + 175.4 + ], + "WL": [ + 1366.2, + 1075.4, + 915.7, + 813.8, + 747.7, + 707.4, + 674.9 + ], + "CR": [ + 1111.7, + 882.2, + 754.7, + 678, + 630.1, + 599.8, + 567.2 + ], + "OC": [ + 1366.4, + 1009.6, + 820.8, + 702.4, + 636.3, + 593.1, + 536.8 + ], + "NS": [ + 1111.7, + 882.2, + 754.7, + 678, + 630.1, + 599.8, + 567.2 + ], + "OL": [ + 1299.8, + 1037, + 892.2, + 800.5, + 743.6, + 712.9, + 687.5 + ], + "R52": [ + 866.3, + 724.9, + 642.2, + 586.7, + 561.7, + 553.2, + 549.9 + ], + "R60": [ + 806.5, + 682.9, + 605, + 562.4, + 542.9, + 534.7, + 530.6 + ], + "R75": [ + 763.8, + 648.6, + 578.1, + 545.2, + 527.6, + 516, + 505.8 + ], + "R90": [ + 770.4, + 649.9, + 575.9, + 541.5, + 522.6, + 506.7, + 486.6 + ], + "R110": [ + 841.2, + 690.9, + 599.3, + 550.4, + 525.7, + 508.1, + 476.4 + ], + "R120": [ + 899.7, + 728.7, + 628.2, + 564.5, + 533.7, + 514.4, + 481.6 + ], + "R135": [ + 1027.7, + 808.1, + 690.6, + 609.2, + 558.7, + 532, + 498 + ], + "R150": [ + 1205.8, + 924.1, + 770, + 674, + 602.7, + 557.4, + 514.8 + ] + } + } + ] +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java new file mode 100644 index 00000000000..39cb7b7216a --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java @@ -0,0 +1,59 @@ +package com.sap.sailing.server.gateway.deserialization.test.racelog; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Iterator; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.common.orc.ORCCertificate; +import com.sap.sailing.domain.orc.ORCCertificatesCollection; +import com.sap.sailing.domain.orc.ORCCertificatesImporter; +import com.sap.sailing.server.gateway.deserialization.racelog.impl.ORCCertificateJsonDeserializer; +import com.sap.sailing.server.gateway.serialization.racelog.impl.ORCCertificateJsonSerializer; + +public class ORCCertificateJsonDeserializerTest { + private ORCCertificateJsonSerializer serializer; + private ORCCertificateJsonDeserializer deserializer; + private static final String RESOURCES = "resources/rms/"; + @Before + public void setup() { + deserializer = new ORCCertificateJsonDeserializer(); + serializer = new ORCCertificateJsonSerializer(); + } + + @Test + public void testOldFormateOrcCertificateDeserializer() throws FileNotFoundException, IOException, ParseException { + File fileGER = new File(RESOURCES + "newFormatCertificate.json"); + ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); + Iterator iterator = importer.getCertificates().iterator(); + assertTrue(iterator.hasNext()); + ORCCertificate certificate = iterator.next(); + JSONObject serializedCertificate =serializer.serialize(certificate); + assertNotNull(serializedCertificate); + ORCCertificate certificateAfterDeserialize = deserializer.deserialize(serializedCertificate); + assertEquals(certificate.getId(), certificateAfterDeserialize.getId()); + } + + @Test + public void testOrcCertificateOldFormatDeserializer() throws IOException, ParseException { + File fileGER = new File(RESOURCES + "oldFormatCertificate.json"); + ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); + Iterator iterator = importer.getCertificates().iterator(); + assertTrue(iterator.hasNext()); + ORCCertificate certificate = iterator.next(); + JSONObject serializedCertificate =serializer.serialize(certificate); + assertNotNull(serializedCertificate); + ORCCertificate certificateAfterDeserialize = deserializer.deserialize(serializedCertificate); + assertEquals(certificate.getId(), certificateAfterDeserialize.getId()); + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java new file mode 100644 index 00000000000..43eff18cfa4 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java @@ -0,0 +1,48 @@ +package com.sap.sailing.server.gateway.serialization.test; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Iterator; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.common.orc.ORCCertificate; +import com.sap.sailing.domain.orc.ORCCertificatesCollection; +import com.sap.sailing.domain.orc.ORCCertificatesImporter; +import com.sap.sailing.server.gateway.serialization.racelog.impl.ORCCertificateJsonSerializer; + +public class ORCCertificateJsonSerializerTest { + private ORCCertificateJsonSerializer serializer; + private static final String RESOURCES = "resources/rms/"; + @Before + public void setUp() { + serializer = new ORCCertificateJsonSerializer(); + } + + @Test + public void testOrcCertificateNewFormatSerializer() throws IOException, ParseException { + File fileGER = new File(RESOURCES + "newFormatCertificate.json"); + ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); + Iterator iterator = importer.getCertificates().iterator(); + assertTrue(iterator.hasNext()); + ORCCertificate certificate = iterator.next(); + JSONObject serializedCertificate =serializer.serialize(certificate); + assertNotNull(serializedCertificate); + } + @Test + public void testOrcCertificateOldFormatSerializer() throws IOException, ParseException { + File fileGER = new File(RESOURCES + "oldFormatCertificate.json"); + ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); + Iterator iterator = importer.getCertificates().iterator(); + assertTrue(iterator.hasNext()); + ORCCertificate certificate = iterator.next(); + JSONObject serializedCertificate =serializer.serialize(certificate); + assertNotNull(serializedCertificate); + } +} From f232d77b3e004ca7aef9dcc748fc087d8d161c45 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Tue, 4 Feb 2020 13:22:38 +0100 Subject: [PATCH 23/85] Bug 5192: Improved condition when to initially show slider contents in past years on mobile --- .../home/mobile/partials/recents/EventsOverviewRecentYear.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java index 7d802044df2..53e377cae77 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java @@ -98,7 +98,7 @@ public class EventsOverviewRecentYear extends Composite { } private void initStageContents() { - if (!isStageInitialized) { + if (isContentVisible && !isStageInitialized) { eventStage.setFeaturedEvents(events); isStageInitialized = true; } From a453cdbde7328e9439f6567bc49a6bdcc85baf06 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Tue, 4 Feb 2020 13:28:33 +0100 Subject: [PATCH 24/85] Bug 5192: Added assert and docs to make clear that the contents of WidgetCarousel are not meant to be dynamic --- .../sap/sse/gwt/client/controls/carousel/WidgetCarousel.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index e6d0458ac76..3dc54f04821 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -192,12 +192,15 @@ public class WidgetCarousel extends Composite { }-*/; /** - * Add new image to carousel. + * Set widgets to be shown by the carousel. * * @param url * */ public void setWidgets(Iterable slides) { + // May only be called once because items added after the initialization of slick slider are not recognized. + // If adding items should be supported, the integration needs to be extended to also update the slider model. + assert items.isEmpty(); for (Widget slide : slides) { items.add(slide); sliderMainUi.add(slide); From cfb6cc0763abea41aff6bdfad6641d39829fbcb2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 13:48:07 +0100 Subject: [PATCH 25/85] refresh the "Local Server" tab when it is shown/switched to --- .../gwt/ui/adminconsole/AdminConsoleEntryPoint.java | 10 +++++++--- .../ui/adminconsole/LocalServerManagementPanel.java | 5 +---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 3316cb7a1ff..f8d4a2eb01d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -399,13 +399,17 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint SecuredSecurityTypes.SERVER.getPermissionForObject( SecuredSecurityTypes.ServerActions.CONFIGURE_REMOTE_INSTANCES, serverInfo)); - LocalServerManagementPanel localServerInstancesManagementPanel = new LocalServerManagementPanel( + final LocalServerManagementPanel localServerInstancesManagementPanel = new LocalServerManagementPanel( getSailingService(), getUserService(), this, getStringMessages()); localServerInstancesManagementPanel.ensureDebugId("LocalServer"); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel( - localServerInstancesManagementPanel), - getStringMessages().localServer(), + localServerInstancesManagementPanel) { + @Override + public void refreshAfterBecomingVisible() { + localServerInstancesManagementPanel.refreshServerConfiguration(); + } + }, getStringMessages().localServer(), // We explicitly use a different permission check here. // Most panels show a list of domain objects which means we check if the user has permissions for any // potentially existing object to decide about the visibility. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java index bdb6d208587..9ebea4f1f7e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java @@ -61,15 +61,12 @@ public class LocalServerManagementPanel extends SimplePanel { this.userService = userService; this.errorReporter = errorReporter; this.stringMessages = stringMessages; - final Panel mainPanel = new VerticalPanel(); setWidget(mainPanel); mainPanel.setWidth("100%"); - mainPanel.add(this.buttonPanel = createServerActionsUi(userService)); mainPanel.add(createServerInfoUI()); mainPanel.add(createServerConfigurationUI()); - refreshServerConfiguration(); } @@ -150,7 +147,7 @@ public class LocalServerManagementPanel extends SimplePanel { }); } - private void refreshServerConfiguration() { + void refreshServerConfiguration() { sailingService.getServerConfiguration(new RefreshAsyncCallback<>(this::updateServerConfiguration)); } From 27279d8df8630373d47710ccbf775743651c0a5b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 13:49:22 +0100 Subject: [PATCH 26/85] updated release notes for "Local Server" tab changes --- java/com.sap.sailing.www/release_notes_admin.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 14105a101a3..8e18f0c97f0 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -21,6 +21,10 @@

Release Notes - Administration Console

+

February 2020

+
    +
  • The "Local Server" tab now refreshes automatically when it comes into view.
  • +

January 2020

  • Overhaul of the Result Import URLs page to be more user friendly and to better fit in with From 34cc3e01c056458c2d920809e651cb94def4afa1 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Tue, 4 Feb 2020 15:59:06 +0100 Subject: [PATCH 27/85] Bug 5192: Fixed lazy initialization of sliders in recent years on mobile --- .../gwt/home/mobile/partials/recents/EventsOverviewRecent.java | 3 +-- .../home/mobile/partials/recents/EventsOverviewRecentYear.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecent.java index 1264ea85478..095e99d72eb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecent.java @@ -37,9 +37,8 @@ public class EventsOverviewRecent extends Composite { List allEvents = new LinkedList(); boolean oneYearIsExpanded = false; for (EventListYearDTO yearDTO : years) { - EventsOverviewRecentYear recentEventsOfOneYear = new EventsOverviewRecentYear(yearDTO, navigator, true); + EventsOverviewRecentYear recentEventsOfOneYear = new EventsOverviewRecentYear(yearDTO, navigator, !oneYearIsExpanded); year.add(recentEventsOfOneYear); - if (oneYearIsExpanded) recentEventsOfOneYear.onHeaderClicked(); oneYearIsExpanded = true; allEvents.addAll(yearDTO.getEvents()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java index 53e377cae77..4f4d3a5256c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/recents/EventsOverviewRecentYear.java @@ -104,7 +104,7 @@ public class EventsOverviewRecentYear extends Composite { } } - void onHeaderClicked() { + private void onHeaderClicked() { isContentVisible = !isContentVisible; updateContentVisibility(); } From 0c4ce87869e3449a07a77291f1f43620c2bc8bdd Mon Sep 17 00:00:00 2001 From: Usman Ali Date: Tue, 4 Feb 2020 22:43:29 +0500 Subject: [PATCH 28/85] Functionality added to parse any allowances custom values for angle --- .../impl/ORCCertificatesCollectionJSON.java | 56 +-- .../resources/rms/newFormatCertificate.json | 322 ++++++++++++++---- .../ORCCertificateJsonDeserializerTest.java | 2 +- 3 files changed, 296 insertions(+), 84 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 9c40258ad4c..1f6abc16492 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -145,19 +145,11 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Map twsMap = new HashMap<>(); for (int i = 0; i < array.size(); i++) { - if ((array.get(i) instanceof JSONObject)) { - JSONObject twa = (JSONObject) array.get(i); - Double twsValue = (Double) twa.get("twsInKts"); - trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue)); - twsMap.put(trueWindSpeedMap.get(twsValue), - new SecondsDurationImpl((Double) twa.get("allInSPNM"))); - } else { - trueWindSpeedMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i].getKnots(), - ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i]); - twsMap.put(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[i], - new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); - } - + JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i, null); + Double tws = (Double) twsAndAllowances.get("tws"); + Double allowance = (Double) twsAndAllowances.get("allowance"); + trueWindSpeedMap.put(tws, new KnotSpeedImpl(tws)); + twsMap.put(trueWindSpeedMap.get(tws), new SecondsDurationImpl(allowance)); } Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue)); @@ -172,15 +164,21 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec final JSONArray array = (JSONArray) allowances.get(aKey); if (((String) aKey).equals(BEAT_ANGLE)) { for (int i = 0; i < array.size(); i++) { - beatAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]), - new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); + JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i, + trueWindSpeedArray); + Double tws = (Double) twsAndAllowances.get("tws"); + Double allowance = (Double) twsAndAllowances.get("allowance"); + beatAngles.put(new KnotSpeedImpl(tws), new DegreeBearingImpl(allowance)); } continue; } if (((String) aKey).equals(GYBE_ANGLE)) { for (int i = 0; i < array.size(); i++) { - gybeAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]), - new DegreeBearingImpl(((Number) array.get(i)).doubleValue())); + JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i, + trueWindSpeedArray); + Double tws = (Double) twsAndAllowances.get("tws"); + Double allowance = (Double) twsAndAllowances.get("allowance"); + gybeAngles.put(new KnotSpeedImpl(tws), new DegreeBearingImpl(allowance)); } continue; } @@ -188,8 +186,11 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec //ignore true wind angles key if (!p.matcher((String) aKey).lookingAt()) { for (int i = 0; i < array.size(); i++) { - twsMap.put(new KnotSpeedImpl(trueWindSpeedArray[i]), - new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); + JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i, + trueWindSpeedArray); + Double tws = (Double) twsAndAllowances.get("tws"); + Double allowance = (Double) twsAndAllowances.get("allowance"); + twsMap.put(new KnotSpeedImpl(tws), new SecondsDurationImpl(allowance)); } } switch ((String) aKey) { @@ -257,6 +258,23 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec } + + private JSONObject getTrueWindSpeedAndAllowances(Object object, int index, Double[] trueWindSpeed) { + + JSONObject response = new JSONObject(); + if ((object instanceof JSONObject)) { + JSONObject twa = (JSONObject) object; + Double twsValue = (Double) twa.get("twsInKts"); + Double allowance = ((Number) twa.get("allInSPNM")).doubleValue(); + response.put("tws", twsValue); + response.put("allowance", allowance); + } else { + response.put("tws", trueWindSpeed == null ? ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[index].getKnots() + : trueWindSpeed[index]); + response.put("allowance", ((Number) object).doubleValue()); + } + return response; + } private String getId(JSONObject certificateAsJson) { return getIdFromFields(certificateAsJson.get(NAT_AUTH).toString(), diff --git a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json index c1fe9e96e27..9195fc6019e 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json +++ b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json @@ -58,86 +58,280 @@ "GPH_DH_TOT":0, "Allowances":{ "Beat":[ - 1036.9, - 864.7, - 780.4, - 754, - 743.7, - 739.4, - 737.3 + { + "twsInKts":7.5, + "allInSPNM":1036.9 + }, + { + "twsInKts":8.5, + "allInSPNM":864.7 + }, + { + "twsInKts":9.5, + "allInSPNM":780.4 + }, + { + "twsInKts":12.5, + "allInSPNM":754 + }, + { + "twsInKts":16.5, + "allInSPNM":743.7 + }, + { + "twsInKts":20.5, + "allInSPNM":739.4 + }, + { + "twsInKts":30.5, + "allInSPNM":737.3 + } + ], "Run":[ - 1049.1, - 843, - 709.9, - 625.2, - 569.2, - 535.4, - 481.8 + { + "twsInKts":7.5, + "allInSPNM":1049.1 + }, + { + "twsInKts":8.5, + "allInSPNM":843 + }, + { + "twsInKts":9.5, + "allInSPNM":709.9 + }, + { + "twsInKts":12.5, + "allInSPNM":625.2 + }, + { + "twsInKts":16.5, + "allInSPNM":569.2 + }, + { + "twsInKts":20.5, + "allInSPNM":535.4 + }, + { + "twsInKts":30.5, + "allInSPNM":481.8 + } ], "BeatAngle":[ - 40.8, - 40.8, - 38.2, - 37.2, - 36.8, - 36.8, - 37.5 + { + "twsInKts":7.5, + "allInSPNM":40.8 + }, + { + "twsInKts":8.5, + "allInSPNM":40.8 + }, + { + "twsInKts":9.5, + "allInSPNM":38.2 + }, + { + "twsInKts":12.5, + "allInSPNM":37.2 + }, + { + "twsInKts":16.5, + "allInSPNM":36.8 + }, + { + "twsInKts":20.5, + "allInSPNM":36.8 + }, + { + "twsInKts":30.5, + "allInSPNM":37.5 + } ], "GybeAngle":[ - 147.5, - 151, - 153.5, - 168.9, - 180, - 180, - 180 + { + "twsInKts":7.5, + "allInSPNM":147.5 + }, + { + "twsInKts":8.5, + "allInSPNM":151 + }, + { + "twsInKts":9.5, + "allInSPNM":153.5 + }, + { + "twsInKts":12.5, + "allInSPNM":168.9 + }, + { + "twsInKts":16.5, + "allInSPNM":180 + }, + { + "twsInKts":20.5, + "allInSPNM":180 + }, + { + "twsInKts":30.5, + "allInSPNM":180 + } ], "WL":[ - 1043, - 853.8, - 745.2, - 689.6, - 656.5, - 637.4, - 609.5 + { + "twsInKts":7.5, + "allInSPNM":1043 + }, + { + "twsInKts":8.5, + "allInSPNM":853.8 + }, + { + "twsInKts":9.5, + "allInSPNM":745.2 + }, + { + "twsInKts":12.5, + "allInSPNM":689.6 + }, + { + "twsInKts":16.5, + "allInSPNM":656.5 + }, + { + "twsInKts":20.5, + "allInSPNM":637.4 + }, + { + "twsInKts":30.5, + "allInSPNM":609.5 + } + ], "CR":[ - 890.9, - 726.2, - 639.4, - 589.6, - 559.1, - 538.7, - 511.2 + { + "twsInKts":7.5, + "allInSPNM":890.9 + }, + { + "twsInKts":8.5, + "allInSPNM":726.2 + }, + { + "twsInKts":9.5, + "allInSPNM":639.4 + }, + { + "twsInKts":12.5, + "allInSPNM":589.6 + }, + { + "twsInKts":16.5, + "allInSPNM":559.1 + }, + { + "twsInKts":20.5, + "allInSPNM":538.7 + }, + { + "twsInKts":30.5, + "allInSPNM":511.2 + } + ], "OC":[ - 1043.6, - 805.8, - 678.6, - 609.8, - 572.4, - 541.2, - 486.8 + { + "twsInKts":7.5, + "allInSPNM":1043.6 + }, + { + "twsInKts":8.5, + "allInSPNM":805.8 + }, + { + "twsInKts":9.5, + "allInSPNM":678.6 + }, + { + "twsInKts":12.5, + "allInSPNM":609.8 + }, + { + "twsInKts":16.5, + "allInSPNM":572.4 + }, + { + "twsInKts":20.5, + "allInSPNM":541.2 + }, + { + "twsInKts":30.5, + "allInSPNM":486.8 + } + ], "NS":[ - 929.4, - 753.6, - 660.1, - 606.1, - 572.9, - 551, - 522.5 + { + "twsInKts":7.5, + "allInSPNM":929.4 + }, + { + "twsInKts":8.5, + "allInSPNM":753.6 + }, + { + "twsInKts":9.5, + "allInSPNM":660.1 + }, + { + "twsInKts":12.5, + "allInSPNM":606.1 + }, + { + "twsInKts":16.5, + "allInSPNM":572.9 + }, + { + "twsInKts":20.5, + "allInSPNM":551 + }, + { + "twsInKts":30.5, + "allInSPNM":522.5 + } + ], "OL":[ - 991.7, - 813.8, - 724.1, - 679.6, - 654.6, - 638.3, - 611.9 - ], + { + "twsInKts":7.5, + "allInSPNM":991.7 + }, + { + "twsInKts":8.5, + "allInSPNM":813.8 + }, + { + "twsInKts":9.5, + "allInSPNM":724.1 + }, + { + "twsInKts":12.5, + "allInSPNM":679.6 + }, + { + "twsInKts":16.5, + "allInSPNM":654.6 + }, + { + "twsInKts":20.5, + "allInSPNM":638.3 + }, + { + "twsInKts":30.5, + "allInSPNM":611.9 + } + ], "R52.1":[ { "twsInKts":7.5, diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java index 39cb7b7216a..b6f492f4325 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/deserialization/test/racelog/ORCCertificateJsonDeserializerTest.java @@ -32,7 +32,7 @@ public class ORCCertificateJsonDeserializerTest { } @Test - public void testOldFormateOrcCertificateDeserializer() throws FileNotFoundException, IOException, ParseException { + public void testNewFormateOrcCertificateDeserializer() throws FileNotFoundException, IOException, ParseException { File fileGER = new File(RESOURCES + "newFormatCertificate.json"); ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); Iterator iterator = importer.getCertificates().iterator(); From 41ab2673c45fedcab9c77180c7bcec702dcbe262 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 18:45:53 +0100 Subject: [PATCH 29/85] bug5194: started by fixing a bad bug causing outbound messaging to stop; when de-registering a replica that caused one or more replicables to no longer be observed, the outbound queue was prematurely closed and set to null. For example, if one replica replicated the full server and another one only the SecurityService, and then the full replica was unregistered, sending security-related operations stopped even though a replica was still registered. --- .../SailingServer (No Proxy).launch | 4 ++-- ...uto-replicate from replica at 8889).launch | 4 ++-- ...ailingServer (No Proxy, winddbTest).launch | 4 ++-- .../impl/ReplicationInstancesManager.java | 4 ++-- .../impl/ReplicationServiceImpl.java | 20 +++++++++++++------ ...licationLeadingToEmailReplicationTest.java | 6 +++--- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index c8b429161a1..6797d2a723c 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -32,9 +32,9 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch index 6c5e7a8138b..e5bb31bef50 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index 15bdf94a15e..1392e9bc254 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -29,9 +29,9 @@ - + - + diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java index 61c16a25ff0..b3cd75d905c 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java @@ -27,9 +27,9 @@ public class ReplicationInstancesManager { * registered slaves, assuming the slaves will have subscribed for the replication topic. Keys are the * {@link ReplicaDescriptor#getUuid() IDs of the corresponding values}. */ - private Map replicaDescriptors; + private final Map replicaDescriptors; - private Map>, Integer>> replicationCounts; + private final Map>, Integer>> replicationCounts; /** * Used to calculate the average number of operations per message sent/received for each replica. diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index 93727d82335..86e788dc003 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -22,6 +22,7 @@ import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -130,7 +131,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM */ private ReplicationReceiverImpl replicator; - private final Map> executionListenersByReplicableIdAsString; + private final ConcurrentMap> executionListenersByReplicableIdAsString; private Thread replicatorThread; @@ -263,7 +264,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM synchronized (replicationInstancesManager) { // .. and at least one of them wants to replicate the replicable with that ID if (Util.contains(replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(), replicable.getId().toString())) { - ensureOperationExecutionListenerAndInjectResetToMasterService(replicable); + ensureOperationExecutionListener(replicable); } } } @@ -297,7 +298,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM throws IOException { timer = new Timer("ReplicationServiceImpl timer for delayed task sending", /* isDaemon */ true); unsentOperationsSenderJob = new UnsentOperationsSenderJob(); - executionListenersByReplicableIdAsString = new HashMap<>(); + executionListenersByReplicableIdAsString = new ConcurrentHashMap<>(); initialLoadChannels = new ConcurrentHashMap<>(); this.replicationInstancesManager = replicationInstancesManager; replicaUUIDs = new HashMap(); @@ -369,8 +370,11 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM @Override public void registerReplica(ReplicaDescriptor replica) throws IOException { synchronized (replicationInstancesManager) { + // due to different replicables to be replicated for replica, ensure that all + // replicables to be replicated to replica are actually observed: + addAsListenerToReplicables(replica.getReplicableIdsAsStrings()); + // need to establish the outbound messaging channel only when this is the first replica to be added: if (!replicationInstancesManager.hasReplicas()) { - addAsListenerToReplicables(replica.getReplicableIdsAsStrings()); synchronized (this) { if (masterChannel == null) { masterChannel = createMasterChannelAndDeclareFanoutExchange(); @@ -385,7 +389,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM private void addAsListenerToReplicables(String[] replicableIdsAsStringForReplicablesToReplicate) { for (final String replicableIdAsStringForReplicableToReplicate : replicableIdsAsStringForReplicablesToReplicate) { Replicable replicable = getReplicable(replicableIdAsStringForReplicableToReplicate, /* wait */ true); - ensureOperationExecutionListenerAndInjectResetToMasterService(replicable); + ensureOperationExecutionListener(replicable); } } @@ -394,7 +398,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM * {@code replicable} and remembered in {@link #executionListenersByReplicableIdAsString}. Otherwise, this method is * a no-op. */ - private void ensureOperationExecutionListenerAndInjectResetToMasterService(Replicable replicable) { + private void ensureOperationExecutionListener(Replicable replicable) { if (!executionListenersByReplicableIdAsString.containsKey(replicable.getId().toString())) { final ReplicationServiceExecutionListener listener = new ReplicationServiceExecutionListener(this, replicable); executionListenersByReplicableIdAsString.put(replicable.getId().toString(), listener); @@ -405,11 +409,15 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM public ReplicaDescriptor unregisterReplica(UUID replicaUuid) throws IOException { logger.info("Unregistering replica with ID " + replicaUuid); synchronized (replicationInstancesManager) { + final boolean hadReplicas = replicationInstancesManager.hasReplicas(); final Iterable oldReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); final ReplicaDescriptor unregisteredReplica = replicationInstancesManager.unregisterReplica(replicaUuid); final Iterable newReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); for (final String idAsStringOfReplicableNoReplicaIsInterestedInAnymore : Util.removeAll(newReplicablesInReplication, Util.addAll(oldReplicablesInReplication, new HashSet<>()))) { removeAsListenerFromReplicable(idAsStringOfReplicableNoReplicaIsInterestedInAnymore); + } + if (hadReplicas && !replicationInstancesManager.hasReplicas()) { + logger.info("Last replica got unregistered. Stopping to send out operations and closing outbound queue."); synchronized (this) { if (masterChannel != null) { masterChannel.getConnection().close(); diff --git a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java index 4b75e60e482..4d592c8f5b6 100644 --- a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java +++ b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SecurityReplicationLeadingToEmailReplicationTest.java @@ -104,9 +104,9 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe @Test public void triggerEmailSendByAddingUserOnMaster() throws UserManagementException, MailException, IllegalAccessException, InterruptedException, UserGroupManagementException { - //TODO IllegalStateExceptions thrown, probably because the two replication services per instance share the - //same message queue, but don't know about each other (unlike actual OSGi setup, where there is only - //one replication service per instance that nows all Replicables) + // TODO IllegalStateExceptions thrown, probably because the two replication services per instance share the + // same message queue, but don't know about each other (unlike actual OSGi setup, where there is only + // one replication service per instance that knows all Replicables) SecurityService masterSecurityService = securitySetUp.getMaster(); final String username = "Ernie"; final String email = "ernie@sesame-street.com"; From cc505f97d5dbeb5f08cccf0758bdb62bc8ec13ca Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 4 Feb 2020 23:51:27 +0100 Subject: [PATCH 30/85] bug5194: added com.sap.sse.replication.persistence to store replica descriptors --- java/com.sap.sse.feature/feature.xml | 9 ++- .../META-INF/MANIFEST.MF | 3 +- .../impl/ReplicaDescriptorImpl.java | 14 +++- .../.classpath | 7 ++ .../.project | 28 ++++++++ .../.settings/org.eclipse.jdt.core.prefs | 7 ++ .../META-INF/MANIFEST.MF | 16 +++++ .../build.properties | 4 ++ .../pom.xml | 12 ++++ .../persistence/DomainObjectFactory.java | 7 ++ .../persistence/MongoObjectFactory.java | 9 +++ .../persistence/PersistenceFactory.java | 17 +++++ .../persistence/impl/Activator.java | 19 ++++++ .../persistence/impl/CollectionNames.java | 5 ++ .../impl/DomainObjectFactoryImpl.java | 64 +++++++++++++++++++ .../persistence/impl/FieldNames.java | 6 ++ .../impl/MongoObjectFactoryImpl.java | 45 +++++++++++++ .../impl/PersistenceFactoryImpl.java | 29 +++++++++ .../META-INF/MANIFEST.MF | 1 + .../sap/sse/replication/impl/Activator.java | 15 ++++- .../impl/ReplicationInstancesManager.java | 15 ++++- .../impl/ReplicationServiceImpl.java | 30 ++++++++- java/pom.xml | 1 + 23 files changed, 353 insertions(+), 10 deletions(-) rename java/{com.sap.sse.replication => com.sap.sse.replication.interfaces}/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java (84%) create mode 100644 java/com.sap.sse.replication.persistence/.classpath create mode 100644 java/com.sap.sse.replication.persistence/.project create mode 100644 java/com.sap.sse.replication.persistence/.settings/org.eclipse.jdt.core.prefs create mode 100644 java/com.sap.sse.replication.persistence/META-INF/MANIFEST.MF create mode 100644 java/com.sap.sse.replication.persistence/build.properties create mode 100644 java/com.sap.sse.replication.persistence/pom.xml create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/DomainObjectFactory.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/MongoObjectFactory.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/PersistenceFactory.java create mode 100755 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/Activator.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/CollectionNames.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/FieldNames.java create mode 100644 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java create mode 100755 java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/PersistenceFactoryImpl.java diff --git a/java/com.sap.sse.feature/feature.xml b/java/com.sap.sse.feature/feature.xml index 815a6e37f2a..cfb50303089 100755 --- a/java/com.sap.sse.feature/feature.xml +++ b/java/com.sap.sse.feature/feature.xml @@ -220,7 +220,7 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al version="0.0.0" unpack="false"/> - + + diff --git a/java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF b/java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF index 764324394d6..794461659e8 100755 --- a/java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF @@ -6,7 +6,8 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Automatic-Module-Name: com.sap.sse.replication.interfaces Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Export-Package: com.sap.sse.replication +Export-Package: com.sap.sse.replication, + com.sap.sse.replication.impl Import-Package: com.rabbitmq.client;version="2.8.4", org.json.simple Require-Bundle: com.sap.sse.operationaltransformation;bundle-version="1.0.0", diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java similarity index 84% rename from java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java rename to java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java index 5fe0b29d542..ec629104e4f 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/impl/ReplicaDescriptorImpl.java @@ -27,10 +27,18 @@ public class ReplicaDescriptorImpl implements ReplicaDescriptor { /** * Sets the registration time to now. */ - public ReplicaDescriptorImpl(InetAddress ipAddress, UUID serverUuid, String additionalInformation, String[] replicableIdsAsStrings) { + public ReplicaDescriptorImpl(InetAddress ipAddress, UUID id, String additionalInformation, String[] replicableIdsAsStrings) { + this(ipAddress, id, MillisecondsTimePoint.now(), additionalInformation, replicableIdsAsStrings); + } + + /** + * Sets the registration time to now. + */ + public ReplicaDescriptorImpl(InetAddress ipAddress, UUID id, TimePoint registrationTime, + String additionalInformation, String[] replicableIdsAsStrings) { assert replicableIdsAsStrings != null && replicableIdsAsStrings.length > 0; - this.uuid = serverUuid; - this.registrationTime = MillisecondsTimePoint.now(); + this.uuid = id; + this.registrationTime = registrationTime; this.ipAddress = ipAddress; this.additionalInformation = additionalInformation; this.replicableIdsAsStrings = replicableIdsAsStrings; diff --git a/java/com.sap.sse.replication.persistence/.classpath b/java/com.sap.sse.replication.persistence/.classpath new file mode 100644 index 00000000000..eca7bdba8f0 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/com.sap.sse.replication.persistence/.project b/java/com.sap.sse.replication.persistence/.project new file mode 100644 index 00000000000..d545251231e --- /dev/null +++ b/java/com.sap.sse.replication.persistence/.project @@ -0,0 +1,28 @@ + + + com.sap.sse.replication.persistence + + + + + + 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.replication.persistence/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sse.replication.persistence/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c68a61dca8 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/.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.replication.persistence/META-INF/MANIFEST.MF b/java/com.sap.sse.replication.persistence/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..9be57b4bc99 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Persistence +Bundle-SymbolicName: com.sap.sse.replication.persistence +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Automatic-Module-Name: com.sap.sse.replication.persistence +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: com.sap.sse.replication.interfaces, + org.mongodb.mongo-java-driver;bundle-version="3.6.4", + com.sap.sse.common +Import-Package: com.sap.sse.mongodb, + org.osgi.framework;version="1.8.0" +Export-Package: com.sap.sse.replication.persistence +Bundle-ActivationPolicy: lazy +Bundle-Activator: com.sap.sse.replication.persistence.impl.Activator diff --git a/java/com.sap.sse.replication.persistence/build.properties b/java/com.sap.sse.replication.persistence/build.properties new file mode 100644 index 00000000000..34d2e4d2dad --- /dev/null +++ b/java/com.sap.sse.replication.persistence/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/java/com.sap.sse.replication.persistence/pom.xml b/java/com.sap.sse.replication.persistence/pom.xml new file mode 100644 index 00000000000..eb58c2febe8 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + root + com.sap.sailing + 1.0.0-SNAPSHOT + + com.sap.sse.replication.persistence + eclipse-plugin + diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/DomainObjectFactory.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/DomainObjectFactory.java new file mode 100644 index 00000000000..22f6cb3cac8 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/DomainObjectFactory.java @@ -0,0 +1,7 @@ +package com.sap.sse.replication.persistence; + +import com.sap.sse.replication.ReplicaDescriptor; + +public interface DomainObjectFactory { + Iterable loadReplicaDescriptors(); +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/MongoObjectFactory.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/MongoObjectFactory.java new file mode 100644 index 00000000000..36543dded21 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/MongoObjectFactory.java @@ -0,0 +1,9 @@ +package com.sap.sse.replication.persistence; + +import com.sap.sse.replication.ReplicaDescriptor; + +public interface MongoObjectFactory { + void storeReplicaDescriptor(ReplicaDescriptor replicaDescriptor); + + void removeReplicaDescriptor(ReplicaDescriptor replicaDescriptor); +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/PersistenceFactory.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/PersistenceFactory.java new file mode 100644 index 00000000000..dc006e37a8f --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/PersistenceFactory.java @@ -0,0 +1,17 @@ +package com.sap.sse.replication.persistence; + +import com.sap.sse.mongodb.MongoDBService; +import com.sap.sse.replication.persistence.impl.PersistenceFactoryImpl; + +public interface PersistenceFactory { + PersistenceFactory INSTANCE = new PersistenceFactoryImpl(); + + DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService); + + MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService); + + DomainObjectFactory getDefaultDomainObjectFactory(); + + MongoObjectFactory getDefaultMongoObjectFactory(); + +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/Activator.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/Activator.java new file mode 100755 index 00000000000..e74b87ddb1a --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/Activator.java @@ -0,0 +1,19 @@ +package com.sap.sse.replication.persistence.impl; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +import com.sap.sse.mongodb.MongoDBService; + +public class Activator implements BundleActivator { + @Override + public void start(BundleContext context) throws Exception { + for (CollectionNames name : CollectionNames.values()) { + MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name()); + } + } + + @Override + public void stop(BundleContext context) throws Exception { + } +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/CollectionNames.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/CollectionNames.java new file mode 100644 index 00000000000..0b79298e166 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/CollectionNames.java @@ -0,0 +1,5 @@ +package com.sap.sse.replication.persistence.impl; + +public enum CollectionNames { + REPLICA_DESCRIPTORS +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java new file mode 100644 index 00000000000..c20b406000f --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java @@ -0,0 +1,64 @@ +package com.sap.sse.replication.persistence.impl; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bson.Document; + +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.replication.ReplicaDescriptor; +import com.sap.sse.replication.impl.ReplicaDescriptorImpl; +import com.sap.sse.replication.persistence.DomainObjectFactory; + +public class DomainObjectFactoryImpl implements DomainObjectFactory { + private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName()); + private final MongoDatabase db; + + public DomainObjectFactoryImpl(MongoDatabase db) { + super(); + this.db = db; + } + + private MongoCollection getReplicaDescriptorCollection() { + final MongoCollection result = db.getCollection(CollectionNames.REPLICA_DESCRIPTORS.name()); + return result; + } + + @Override + public Iterable loadReplicaDescriptors() { + final Map result = new HashMap<>(); + for (final Document o : getReplicaDescriptorCollection().find()) { + final ReplicaDescriptor replicaDescriptor = loadReplicaDescriptor(o); + if (replicaDescriptor != null) { + result.put(replicaDescriptor.getUuid(), replicaDescriptor); + } + } + return result.values(); + } + + private ReplicaDescriptor loadReplicaDescriptor(Document o) { + final UUID uuid = UUID.fromString(o.getString(FieldNames.REPLICA_ID_AS_STRING.name())); + InetAddress ipAddress; + String ipAddressString = o.getString(FieldNames.REPLICA_IP_ADDRESS.name()); + try { + ipAddress = InetAddress.getByName(ipAddressString); + } catch (UnknownHostException e) { + final String message = "Internal error: the IP address "+ipAddressString+ + " cannot be converted into an InetAddress"; + logger.log(Level.WARNING, message, e); + throw new RuntimeException(message, e); + } + final TimePoint registrationTime = new MillisecondsTimePoint(o.getLong(FieldNames.REPLICA_REGISTRATION_TIME_MILLIS)); + final String additionalInformation = o.getString(FieldNames.REPLICA_ADDITIONAL_INFORMATION.name()); + final String[] replicableIdsAsStrings = o.get(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), new String[0]); + return new ReplicaDescriptorImpl(ipAddress, uuid, registrationTime, additionalInformation, replicableIdsAsStrings); + } +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/FieldNames.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/FieldNames.java new file mode 100644 index 00000000000..c4961e96ff9 --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/FieldNames.java @@ -0,0 +1,6 @@ +package com.sap.sse.replication.persistence.impl; + +public enum FieldNames { + REPLICA_ID_AS_STRING, REPLICA_IP_ADDRESS, REPLICA_REGISTRATION_TIME_MILLIS, + REPLICA_ADDITIONAL_INFORMATION, REPLICA_REPLICABLE_IDS_AS_STRINGS; +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java new file mode 100644 index 00000000000..cbdd019c47e --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java @@ -0,0 +1,45 @@ +package com.sap.sse.replication.persistence.impl; + +import org.bson.Document; + +import com.mongodb.WriteConcern; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.UpdateOptions; +import com.sap.sse.replication.ReplicaDescriptor; +import com.sap.sse.replication.persistence.MongoObjectFactory; + +public class MongoObjectFactoryImpl implements MongoObjectFactory { + private final MongoDatabase db; + + public MongoObjectFactoryImpl(MongoDatabase db) { + super(); + this.db = db; + } + + private MongoCollection getReplicaDescriptorCollection() { + final MongoCollection result = db.getCollection(CollectionNames.REPLICA_DESCRIPTORS.name()); + result.createIndex(new Document(FieldNames.REPLICA_ID_AS_STRING.name(), 1)); + return result; + } + + @Override + public void storeReplicaDescriptor(ReplicaDescriptor replicaDescriptor) { + final Document replicaDescriptorDoc = new Document(); + replicaDescriptorDoc.put(FieldNames.REPLICA_ID_AS_STRING.name(), replicaDescriptor.getUuid().toString()); + replicaDescriptorDoc.put(FieldNames.REPLICA_IP_ADDRESS.name(), replicaDescriptor.getIpAddress().getHostAddress()); + replicaDescriptorDoc.put(FieldNames.REPLICA_REGISTRATION_TIME_MILLIS.name(), replicaDescriptor.getRegistrationTime().asMillis()); + replicaDescriptorDoc.put(FieldNames.REPLICA_ADDITIONAL_INFORMATION.name(), replicaDescriptor.getAdditionalInformation()); + replicaDescriptorDoc.put(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), replicaDescriptor.getReplicableIdsAsStrings()); + getReplicaDescriptorCollection().withWriteConcern(WriteConcern.ACKNOWLEDGED).replaceOne(replicaDescriptorDoc, + replicaDescriptorDoc, new UpdateOptions().upsert(true)); + } + + @Override + public void removeReplicaDescriptor(ReplicaDescriptor replicaDescriptor) { + if (replicaDescriptor != null) { + final Document replicaDescriptorDoc = new Document(FieldNames.REPLICA_ID_AS_STRING.name(), replicaDescriptor.getUuid().toString()); + getReplicaDescriptorCollection().withWriteConcern(WriteConcern.ACKNOWLEDGED).deleteOne(replicaDescriptorDoc); + } + } +} diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/PersistenceFactoryImpl.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/PersistenceFactoryImpl.java new file mode 100755 index 00000000000..932b02e982c --- /dev/null +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/PersistenceFactoryImpl.java @@ -0,0 +1,29 @@ +package com.sap.sse.replication.persistence.impl; + +import com.sap.sse.replication.persistence.PersistenceFactory; +import com.sap.sse.mongodb.MongoDBConfiguration; +import com.sap.sse.mongodb.MongoDBService; +import com.sap.sse.replication.persistence.DomainObjectFactory; +import com.sap.sse.replication.persistence.MongoObjectFactory; + +public class PersistenceFactoryImpl implements PersistenceFactory { + @Override + public DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDbService) { + return new DomainObjectFactoryImpl(mongoDbService.getDB()); + } + + @Override + public MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDbService) { + return new MongoObjectFactoryImpl(mongoDbService.getDB()); + } + + @Override + public DomainObjectFactory getDefaultDomainObjectFactory() { + return getDomainObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService()); + } + + @Override + public MongoObjectFactory getDefaultMongoObjectFactory() { + return getMongoObjectFactory(MongoDBConfiguration.getDefaultConfiguration().getService()); + } +} diff --git a/java/com.sap.sse.replication/META-INF/MANIFEST.MF b/java/com.sap.sse.replication/META-INF/MANIFEST.MF index ad40d0c2f19..867abc1a81a 100644 --- a/java/com.sap.sse.replication/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.replication/META-INF/MANIFEST.MF @@ -24,6 +24,7 @@ Bundle-ClassPath: . Comment: The Web-ContextPath is referred to by ReplicationMasterDescriptorImpl.REPLICATION_SERVLET Web-ContextPath: /replication Import-Package: com.rabbitmq.client;version="2.8.4", + com.sap.sse.replication.persistence, javax.servlet;version="2.6.0", javax.servlet.http;version="2.6.0", org.osgi.framework;version="1.6.0", diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java index 9a96dfe8ba4..0416c62e17e 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java @@ -4,15 +4,18 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import com.sap.sse.replication.ReplicaDescriptor; import com.sap.sse.replication.Replicable; import com.sap.sse.replication.ReplicablesProvider; import com.sap.sse.replication.ReplicationService; +import com.sap.sse.replication.persistence.PersistenceFactory; import com.sap.sse.security.util.RemoteServerUtil; /** @@ -80,6 +83,7 @@ public class Activator implements BundleActivator { public static final String ENV_VAR_NAME_REPLICATION_PORT = "REPLICATION_PORT"; public static final String PROPERTY_NAME_REPLICATE_ON_START = "replicate.on.start"; + public static final String PROPERTY_NAME_RESTORE_REPLICAS = "replicate.restore.replicas"; public static final String PROPERTY_NAME_REPLICATE_MASTER_SERVLET_HOST = "replicate.master.servlet.host"; public static final String PROPERTY_NAME_REPLICATE_MASTER_SERVLET_PORT = "replicate.master.servlet.port"; public static final String PROPERTY_NAME_REPLICATE_MASTER_QUEUE_HOST = "replicate.master.queue.host"; @@ -127,11 +131,18 @@ public class Activator implements BundleActivator { } catch (NumberFormatException nfe) { logger.severe("Couldn't parse the replication port specification \""+exchangePortAsString+"\". Using default."); } - replicationInstancesManager = new ReplicationInstancesManager(); + final Iterable replicasToAssumeConnectedToThisMaster; + if (Boolean.valueOf(System.getProperty(PROPERTY_NAME_RESTORE_REPLICAS, "false"))) { + replicasToAssumeConnectedToThisMaster = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory().loadReplicaDescriptors(); + } else { + replicasToAssumeConnectedToThisMaster = null; + } + replicationInstancesManager = new ReplicationInstancesManager(replicasToAssumeConnectedToThisMaster); final OSGiReplicableTracker replicablesProvider = new OSGiReplicableTracker(bundleContext); serverReplicationMasterService = new ReplicationServiceImpl( + Optional.of(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory()), exchangeName, exchangeHost, exchangePort, replicationInstancesManager, replicablesProvider); - String replicateOnStart = System.getProperty(PROPERTY_NAME_REPLICATE_ON_START); + final String replicateOnStart = System.getProperty(PROPERTY_NAME_REPLICATE_ON_START); final boolean autoReplicationRequested = replicateOnStart != null && !replicateOnStart.isEmpty(); if (autoReplicationRequested) { // set before registering the service; clients discovering it therefore cannot accidentally diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java index b3cd75d905c..e804ef0b51a 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java @@ -56,13 +56,22 @@ public class ReplicationInstancesManager { private ReplicationMasterDescriptor replicationMasterDescriptor; public ReplicationInstancesManager() { + this(/* replicas to assume connected to this master */ null); + } + + public ReplicationInstancesManager(Iterable replicasToAssumeConnectedToThisMaster) { replicaDescriptors = new HashMap<>(); replicationCounts = new HashMap>,Integer>>(); totalMessageCount = new HashMap<>(); totalNumberOfOperations = new HashMap<>(); totalQueueMessagesRawSizeInBytes = new HashMap(); + if (replicasToAssumeConnectedToThisMaster != null) { + for (final ReplicaDescriptor replicaDescriptor : replicasToAssumeConnectedToThisMaster) { + registerReplica(replicaDescriptor); + } + } } - + /** * Tells if at least one replica is currently registered. * @@ -76,6 +85,10 @@ public class ReplicationInstancesManager { public Iterable getReplicaDescriptors() { return Collections.unmodifiableCollection(replicaDescriptors.values()); } + + public ReplicaDescriptor getReplicaDescriptor(UUID replicaId) { + return replicaDescriptors.get(replicaId); + } public ReplicationMasterDescriptor getReplicationMasterDescriptor() { return replicationMasterDescriptor; diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index 86e788dc003..4f6e1a0b795 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -17,6 +17,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.Timer; import java.util.TimerTask; @@ -47,6 +48,7 @@ import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationReceiver; import com.sap.sse.replication.ReplicationService; import com.sap.sse.replication.ReplicationStatus; +import com.sap.sse.replication.persistence.MongoObjectFactory; import com.sap.sse.util.HttpUrlConnectionHelper; import net.jpountz.lz4.LZ4BlockInputStream; @@ -96,7 +98,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM /** * The UUIDs with which this replica is registered by the master identified by the corresponding key */ - private final Map replicaUUIDs; + private final ConcurrentMap replicaUUIDs; /** * Channel used by a master server to publish replication operations; null in servers that don't have @@ -232,6 +234,13 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM */ private boolean replicationStarting; + /** + * An optional link to a persistence layer that allows this service to record replicas + * registered at this server, so that optionally during a re-start those replica + * links can be re-established. + */ + private final Optional mongoObjectFactory; + private static class InitialLoadRequest { private final Channel channelForInitialLoad; private final Iterable> replicables; @@ -296,12 +305,19 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM public ReplicationServiceImpl(String exchangeName, String exchangeHost, int exchangePort, final ReplicationInstancesManager replicationInstancesManager, ReplicablesProvider replicablesProvider) throws IOException { + this(/* defaultMongoObjectFactory */ Optional.empty(), exchangeName, exchangeHost, exchangePort, replicationInstancesManager, replicablesProvider); + } + + public ReplicationServiceImpl(Optional optionalMongoObjectFactory, String exchangeName, + String exchangeHost, int exchangePort, ReplicationInstancesManager replicationInstancesManager, + ReplicablesProvider replicablesProvider) { + this.mongoObjectFactory = optionalMongoObjectFactory; timer = new Timer("ReplicationServiceImpl timer for delayed task sending", /* isDaemon */ true); unsentOperationsSenderJob = new UnsentOperationsSenderJob(); executionListenersByReplicableIdAsString = new ConcurrentHashMap<>(); initialLoadChannels = new ConcurrentHashMap<>(); this.replicationInstancesManager = replicationInstancesManager; - replicaUUIDs = new HashMap(); + replicaUUIDs = new ConcurrentHashMap(); this.replicablesProvider = replicablesProvider; this.exchangeName = exchangeName; this.exchangeHost = exchangeHost; @@ -382,10 +398,19 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM } } replicationInstancesManager.registerReplica(replica); + recordRegisteredReplicaPersistently(replica); } logger.info("Registered replica " + replica); } + private void recordRegisteredReplicaPersistently(ReplicaDescriptor replica) { + mongoObjectFactory.ifPresent(mof->mof.storeReplicaDescriptor(replica)); + } + + private void removeRegisteredReplicaPersistently(ReplicaDescriptor replica) { + mongoObjectFactory.ifPresent(mof->mof.removeReplicaDescriptor(replica)); + } + private void addAsListenerToReplicables(String[] replicableIdsAsStringForReplicablesToReplicate) { for (final String replicableIdAsStringForReplicableToReplicate : replicableIdsAsStringForReplicablesToReplicate) { Replicable replicable = getReplicable(replicableIdAsStringForReplicableToReplicate, /* wait */ true); @@ -411,6 +436,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM synchronized (replicationInstancesManager) { final boolean hadReplicas = replicationInstancesManager.hasReplicas(); final Iterable oldReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); + removeRegisteredReplicaPersistently(replicationInstancesManager.getReplicaDescriptor(replicaUuid)); final ReplicaDescriptor unregisteredReplica = replicationInstancesManager.unregisterReplica(replicaUuid); final Iterable newReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); for (final String idAsStringOfReplicableNoReplicaIsInterestedInAnymore : Util.removeAll(newReplicablesInReplication, Util.addAll(oldReplicablesInReplication, new HashSet<>()))) { diff --git a/java/pom.xml b/java/pom.xml index f5f7dd492a3..b9a9e8f4526 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -101,6 +101,7 @@ com.sap.sse.security.userstore.mongodb com.sap.sse.operationaltransformation com.sap.sse.replication + com.sap.sse.replication.persistence com.sap.sse.replication.interfaces com.sap.sse.threadmanager com.sap.sse.filestorage From 02478ea33a758d07cc1214a9f943f197fb22b2b5 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 00:48:17 +0100 Subject: [PATCH 31/85] added replication.persistence bundle to launch configurations --- .../SailingServer (No Proxy OSX).launch | 2 +- java/com.sap.sailing.server/SailingServer (No Proxy).launch | 2 +- .../SailingServer (No Proxy, Cached MTB).launch | 2 +- .../SailingServer (No Proxy, Igtimi Local Test).launch | 2 +- .../SailingServer (No Proxy, Java11).launch | 2 +- .../SailingServer (No Proxy, Java11, JFR Recording).launch | 2 +- .../SailingServer (No Proxy, Jetty on 8889).launch | 2 +- ...SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch | 2 +- ...erver (No Proxy, Jetty on 8889, auto-replicate dev).launch | 2 +- ...ingServer (No Proxy, Jetty on 8889, auto-replicate).launch | 2 +- .../SailingServer (No Proxy, Jetty on 8890).launch | 2 +- ...Jetty on 8890, auto-replicate from replica at 8889).launch | 4 ++-- .../SailingServer (No Proxy, Remote Debug SAP VM).launch | 2 +- .../SailingServer (No Proxy, bundesliga2-2017 DB).launch | 2 +- .../SailingServer (No Proxy, winddbTest).launch | 2 +- ... on 8889, Polar & Wind estimation Import from 8888).launch | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch index 1c157e51579..709fd3faa8a 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch @@ -34,5 +34,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 6797d2a723c..2ff74ea126d 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -36,5 +36,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch index e6586a7228a..3812924b04c 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch @@ -33,5 +33,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch index 17c0de68e2b..2ad5ce9bfd4 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Igtimi Local Test).launch @@ -36,5 +36,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch index 1706706515f..88927584a41 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11).launch @@ -36,5 +36,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch index 815b877f526..39fb0441f9f 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Java11, JFR Recording).launch @@ -36,5 +36,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index c00770c580d..5fa33f3a720 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -27,6 +27,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch index cf70faf34a6..79c22a1f79a 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch @@ -27,6 +27,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch index a1b808e3f56..c20153d2906 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate dev).launch @@ -27,6 +27,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch index 74a3f381146..f34bb420636 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch @@ -29,6 +29,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch index 838862635a0..45e2a84a909 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890).launch @@ -27,6 +27,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch index e5bb31bef50..8d7495a6653 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8890, auto-replicate from replica at 8889).launch @@ -23,10 +23,10 @@ - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch index ac5075acd28..d4a3c3c14bb 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch @@ -27,5 +27,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch index 488ede31dff..bcafeead5fa 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, bundesliga2-2017 DB).launch @@ -36,5 +36,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index 1392e9bc254..a2b9d993263 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -33,5 +33,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar & Wind estimation Import from 8888).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar & Wind estimation Import from 8888).launch index 2784a0ea501..7a709529a86 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar & Wind estimation Import from 8888).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889, Polar & Wind estimation Import from 8888).launch @@ -27,6 +27,6 @@ - + From 493b958239bf51ef00b1cc9c5781861254599673 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 01:36:59 +0100 Subject: [PATCH 32/85] bug5194: implementation of replica persistence that works locally --- ...Proxy, Jetty on 8889, auto-replicate).launch | 4 ++-- .../impl/DomainObjectFactoryImpl.java | 5 +++-- .../impl/MongoObjectFactoryImpl.java | 5 ++++- .../com/sap/sse/replication/impl/Activator.java | 4 ++-- .../impl/ReplicationInstancesManager.java | 9 --------- .../impl/ReplicationServiceImpl.java | 17 +++++++++++++++-- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch index f34bb420636..c88efedc996 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch @@ -25,10 +25,10 @@ - + - + diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java index c20b406000f..0d576876e48 100644 --- a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/DomainObjectFactoryImpl.java @@ -2,6 +2,7 @@ package com.sap.sse.replication.persistence.impl; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -56,9 +57,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { logger.log(Level.WARNING, message, e); throw new RuntimeException(message, e); } - final TimePoint registrationTime = new MillisecondsTimePoint(o.getLong(FieldNames.REPLICA_REGISTRATION_TIME_MILLIS)); + final TimePoint registrationTime = new MillisecondsTimePoint(o.getLong(FieldNames.REPLICA_REGISTRATION_TIME_MILLIS.name())); final String additionalInformation = o.getString(FieldNames.REPLICA_ADDITIONAL_INFORMATION.name()); - final String[] replicableIdsAsStrings = o.get(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), new String[0]); + final String[] replicableIdsAsStrings = o.get(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), new ArrayList()).toArray(new String[0]); return new ReplicaDescriptorImpl(ipAddress, uuid, registrationTime, additionalInformation, replicableIdsAsStrings); } } diff --git a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java index cbdd019c47e..82f5d8beb6a 100644 --- a/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sse.replication.persistence/src/com/sap/sse/replication/persistence/impl/MongoObjectFactoryImpl.java @@ -1,5 +1,7 @@ package com.sap.sse.replication.persistence.impl; +import java.util.Arrays; + import org.bson.Document; import com.mongodb.WriteConcern; @@ -30,7 +32,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { replicaDescriptorDoc.put(FieldNames.REPLICA_IP_ADDRESS.name(), replicaDescriptor.getIpAddress().getHostAddress()); replicaDescriptorDoc.put(FieldNames.REPLICA_REGISTRATION_TIME_MILLIS.name(), replicaDescriptor.getRegistrationTime().asMillis()); replicaDescriptorDoc.put(FieldNames.REPLICA_ADDITIONAL_INFORMATION.name(), replicaDescriptor.getAdditionalInformation()); - replicaDescriptorDoc.put(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), replicaDescriptor.getReplicableIdsAsStrings()); + replicaDescriptorDoc.put(FieldNames.REPLICA_REPLICABLE_IDS_AS_STRINGS.name(), + Arrays.asList(replicaDescriptor.getReplicableIdsAsStrings())); getReplicaDescriptorCollection().withWriteConcern(WriteConcern.ACKNOWLEDGED).replaceOne(replicaDescriptorDoc, replicaDescriptorDoc, new UpdateOptions().upsert(true)); } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java index 0416c62e17e..7b5c7052275 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java @@ -137,9 +137,9 @@ public class Activator implements BundleActivator { } else { replicasToAssumeConnectedToThisMaster = null; } - replicationInstancesManager = new ReplicationInstancesManager(replicasToAssumeConnectedToThisMaster); + replicationInstancesManager = new ReplicationInstancesManager(); final OSGiReplicableTracker replicablesProvider = new OSGiReplicableTracker(bundleContext); - serverReplicationMasterService = new ReplicationServiceImpl( + serverReplicationMasterService = new ReplicationServiceImpl(replicasToAssumeConnectedToThisMaster, Optional.of(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory()), exchangeName, exchangeHost, exchangePort, replicationInstancesManager, replicablesProvider); final String replicateOnStart = System.getProperty(PROPERTY_NAME_REPLICATE_ON_START); diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java index e804ef0b51a..301b89588dd 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java @@ -56,20 +56,11 @@ public class ReplicationInstancesManager { private ReplicationMasterDescriptor replicationMasterDescriptor; public ReplicationInstancesManager() { - this(/* replicas to assume connected to this master */ null); - } - - public ReplicationInstancesManager(Iterable replicasToAssumeConnectedToThisMaster) { replicaDescriptors = new HashMap<>(); replicationCounts = new HashMap>,Integer>>(); totalMessageCount = new HashMap<>(); totalNumberOfOperations = new HashMap<>(); totalQueueMessagesRawSizeInBytes = new HashMap(); - if (replicasToAssumeConnectedToThisMaster != null) { - for (final ReplicaDescriptor replicaDescriptor : replicasToAssumeConnectedToThisMaster) { - registerReplica(replicaDescriptor); - } - } } /** diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index 4f6e1a0b795..d140fda244e 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -305,12 +305,20 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM public ReplicationServiceImpl(String exchangeName, String exchangeHost, int exchangePort, final ReplicationInstancesManager replicationInstancesManager, ReplicablesProvider replicablesProvider) throws IOException { - this(/* defaultMongoObjectFactory */ Optional.empty(), exchangeName, exchangeHost, exchangePort, replicationInstancesManager, replicablesProvider); + this(/* defaultMongoObjectFactory */ Optional.empty(), exchangeName, exchangeHost, exchangePort, + replicationInstancesManager, replicablesProvider); } public ReplicationServiceImpl(Optional optionalMongoObjectFactory, String exchangeName, String exchangeHost, int exchangePort, ReplicationInstancesManager replicationInstancesManager, - ReplicablesProvider replicablesProvider) { + ReplicablesProvider replicablesProvider) throws IOException { + this(/* replicasToAssumeConnectedToThisMaster */ null, optionalMongoObjectFactory, + exchangeName, exchangeHost, exchangePort, replicationInstancesManager, replicablesProvider); + } + + public ReplicationServiceImpl(Iterable replicasToAssumeConnectedToThisMaster, + Optional optionalMongoObjectFactory, String exchangeName, String exchangeHost, int exchangePort, + ReplicationInstancesManager replicationInstancesManager, ReplicablesProvider replicablesProvider) throws IOException { this.mongoObjectFactory = optionalMongoObjectFactory; timer = new Timer("ReplicationServiceImpl timer for delayed task sending", /* isDaemon */ true); unsentOperationsSenderJob = new UnsentOperationsSenderJob(); @@ -326,6 +334,11 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM replicator = null; serverUUID = UUID.randomUUID(); logger.info("Setting " + serverUUID.toString() + " as unique replication identifier."); + if (replicasToAssumeConnectedToThisMaster != null) { + for (final ReplicaDescriptor replica : replicasToAssumeConnectedToThisMaster) { + registerReplica(replica); + } + } } @Override From c4c8d100d98a217b14e1ba7a3701600cdc31fc4b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 11:18:58 +0100 Subject: [PATCH 33/85] increased log file size by one order or magnitude# --- java/target/configuration/logging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/target/configuration/logging.properties b/java/target/configuration/logging.properties index f0bbd11e77a..c8044f9f153 100644 --- a/java/target/configuration/logging.properties +++ b/java/target/configuration/logging.properties @@ -7,7 +7,7 @@ handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler # fileHandler specific properties java.util.logging.FileHandler.level = ALL java.util.logging.FileHandler.pattern = logs/sailing%u.log -java.util.logging.FileHandler.limit = 50000000 +java.util.logging.FileHandler.limit = 500000000 java.util.logging.FileHandler.count = 10 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter From 056ae063f3ec9371fb33b8468e074baee1107f9b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 11:21:33 +0100 Subject: [PATCH 34/85] added release notes for the new replicate.restore.replicas system property --- ...r (No Proxy, Jetty on 8889, auto-replicate).launch | 4 ++-- java/com.sap.sailing.www/release_notes_admin.html | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch index c88efedc996..f34bb420636 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate).launch @@ -25,10 +25,10 @@ - + - + diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 8e18f0c97f0..4ebc91df21d 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -24,6 +24,17 @@

    February 2020

    • The "Local Server" tab now refreshes automatically when it comes into view.
    • +
    • There is a new system property repliate.restore.replicas. When it is set to true, + upon start-up the server instance will read the replica descriptors stored persistently when + a replica registers, assuming that those replicas are still alive and want to continue to + receive replication updates from this server. This way, replicas may not have to be + re-started or re-connected to the re-started master instance. Note, though, that + as the re-started master loads races, those loading activities will be broadcast + to all replicas which are likely to have all that information already. Use the flag, e.g., + in the ADDITIONAL_JAVA_ARGS environment variable that the start script + will consider, as in
      +                              ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Drepliate.restore.replicas=true"
      +                            

    January 2020

      From e78285c2f4edd48a494cb084e38178fd85e6a027 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 11:45:42 +0100 Subject: [PATCH 35/85] changing replicate.restore.replicas default to true --- .../src/com/sap/sse/replication/impl/Activator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java index 7b5c7052275..587587aab6c 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java @@ -132,7 +132,7 @@ public class Activator implements BundleActivator { logger.severe("Couldn't parse the replication port specification \""+exchangePortAsString+"\". Using default."); } final Iterable replicasToAssumeConnectedToThisMaster; - if (Boolean.valueOf(System.getProperty(PROPERTY_NAME_RESTORE_REPLICAS, "false"))) { + if (Boolean.valueOf(System.getProperty(PROPERTY_NAME_RESTORE_REPLICAS, "true"))) { replicasToAssumeConnectedToThisMaster = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory().loadReplicaDescriptors(); } else { replicasToAssumeConnectedToThisMaster = null; From ba8f35912c418ec9e5f77d1fc33d50599f264af4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 15:27:19 +0100 Subject: [PATCH 36/85] fixing size of images for right handle of slick widget carousel --- .../sse/gwt/client/controls/carousel/ImageCarousel.ui.xml | 6 ++---- .../sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml index d6e996a579c..1213e9ee667 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -104,8 +104,8 @@ -moz-transform: translate(0, -50%); -ms-transform: translate(0, -50%); transform: translate(0, -50%); - width: 4em; - height: 4em; + width: 60px; + height: 60px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxNzkyQ0E2OUQ0MzgxMUUzQTM5MEI2NkI4RTRCODhEMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxNzkyQ0E2QUQ0MzgxMUUzQTM5MEI2NkI4RTRCODhEMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE3OTJDQTY3RDQzODExRTNBMzkwQjY2QjhFNEI4OEQzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE3OTJDQTY4RDQzODExRTNBMzkwQjY2QjhFNEI4OEQzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+8Qas9QAAA/9JREFUeNrs3T1LW1EYwHFzk2poS1sthSJipRrFRcEX0CWmgkMHv0YHd2dxlDsGAv0IfgIRlYIgWYpBF4cW0SjokFYL0oY0L80zZKlx8t5zntzz/4Mf4Dm/E5PDzb2JTU9Pf+6iyOaxBAATwAQwAUwAE8AEMMAEMAFMABPABDDpBk4kEjH5i8qiaprH07AYuVzuQzabzUQBWWaQWWQmDfN4GnCnpqZGJicnhyYmJl51OrDMILPITBqQ4/39/cu2cSuVyt+1tbWtg4ODUqcDX11dlYvF4nU6nX4/MDDwpjnfy62trbN6ve4OcDvcnZ2d66i8B5+ent5pQTYOHHVcbchGgV3B1YRsDNg1XC3IRoBdxX0Iufkp+8X29va5CeTQgVvnwpmZmZSLuO2QBwcHjSGHCtzCnZ2ddRrXJnJowODqQA4FGFw9yIEDg6sLOVBgcPUhB36xIR6Px6DTs3aBvoJl58khXg7zct6TnSk7VHYqfPdbWlp6u76+/rG7u/vJ4eHh95WVlS/VarWh+j0YZD24oX2KBlkHbqjnYJDt44YKDLJ93NCBQbaLawQYZHu4xoBdRraJaxTYRWTbuMaBXULWgGsF2AVkLbjWgB9CLhQK5/K94k7Gbc7T6/v+sgZcKWFzMWRwWYBcLtdVq9Uax8fHt53+6pUZjo6OzuTCgW1cKabhWZWt2ztsL0YU50loWJCowGqch/uDIx7AABPABDABTAATwAQwwAQwAUwAE8D0qORy4TTLwCuYACaACWACmAAmgAEmgAlgApgAJoAJYIAtJffSRukhpprm8TQshu/7QxsbG++igCwzyCwyk4Z5PA246XS6b25urnd0dLSn04FlBplFZtKALM/o+GQbt1wu11dXV78VCoXfnQ5cKpWqJycnd4uLi33Dw8NPx8bGenZ3d381Gg13gNvh5vP5yDxG6fLysqIF2Thw1HG1IRsFdgVXE7IxYNdwtSAbAXYV9yHkVCrVvbe3ZwQ5dODWuXBhYeG1i7jtkEdGRp6ZQg4VuIWbyWScxrWJHBowuDqQQwEGVw9y4MDg6kIOFBhcfciBX2zwPI/fLlS0doG+gmXnySFeDvNy3pOdKTtUdip895ufn3/u+34qmUx6+/v7P5v/8c7kudmq34NB1oMb2qdokHXghnoOBtk+bqjAINvHDR0YZLu4RoBBtodrDNhlZJu4RoFdRLaNaxzYJWQNuFaAXUDWgmsN+CHkfD5/K98r7mTc8fHxZDabHdOAaxX4f+SLi4s/m5ubP2x9QTyobm5uak3knmKxWLaNK8lvF361vSit2ztsL0YU51Hx24VRgdU4D/cHRzyAASaACWACmAAmgAlggAlgApgAJoDpUf0TYADnqrAanCLw9QAAAABJRU5ErkJggg==) } @@ -119,8 +119,6 @@ .slider .slick-prev { left: 0px; - height: 60px; - width: 60px; background-position: 0 0; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml index 20b8164d448..3e6b12d3dc9 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -25,8 +25,6 @@ .slider .slick-prev { left: 0px; - height: 60px; - width: 60px; } .slick-prev, .slick-next { @@ -39,8 +37,8 @@ -moz-transform: translate(0, -50%); -ms-transform: translate(0, -50%); transform: translate(0, -50%); - width: 4em; - height: 4em; + height: 60px; + width: 60px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxNzkyQ0E2OUQ0MzgxMUUzQTM5MEI2NkI4RTRCODhEMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxNzkyQ0E2QUQ0MzgxMUUzQTM5MEI2NkI4RTRCODhEMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE3OTJDQTY3RDQzODExRTNBMzkwQjY2QjhFNEI4OEQzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE3OTJDQTY4RDQzODExRTNBMzkwQjY2QjhFNEI4OEQzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+8Qas9QAAA/9JREFUeNrs3T1LW1EYwHFzk2poS1sthSJipRrFRcEX0CWmgkMHv0YHd2dxlDsGAv0IfgIRlYIgWYpBF4cW0SjokFYL0oY0L80zZKlx8t5zntzz/4Mf4Dm/E5PDzb2JTU9Pf+6iyOaxBAATwAQwAUwAE8AEMMAEMAFMABPABDDpBk4kEjH5i8qiaprH07AYuVzuQzabzUQBWWaQWWQmDfN4GnCnpqZGJicnhyYmJl51OrDMILPITBqQ4/39/cu2cSuVyt+1tbWtg4ODUqcDX11dlYvF4nU6nX4/MDDwpjnfy62trbN6ve4OcDvcnZ2d66i8B5+ent5pQTYOHHVcbchGgV3B1YRsDNg1XC3IRoBdxX0Iufkp+8X29va5CeTQgVvnwpmZmZSLuO2QBwcHjSGHCtzCnZ2ddRrXJnJowODqQA4FGFw9yIEDg6sLOVBgcPUhB36xIR6Px6DTs3aBvoJl58khXg7zct6TnSk7VHYqfPdbWlp6u76+/rG7u/vJ4eHh95WVlS/VarWh+j0YZD24oX2KBlkHbqjnYJDt44YKDLJ93NCBQbaLawQYZHu4xoBdRraJaxTYRWTbuMaBXULWgGsF2AVkLbjWgB9CLhQK5/K94k7Gbc7T6/v+sgZcKWFzMWRwWYBcLtdVq9Uax8fHt53+6pUZjo6OzuTCgW1cKabhWZWt2ztsL0YU50loWJCowGqch/uDIx7AABPABDABTAATwAQwwAQwAUwAE8D0qORy4TTLwCuYACaACWACmAAmgAEmgAlgApgAJoAJYIAtJffSRukhpprm8TQshu/7QxsbG++igCwzyCwyk4Z5PA246XS6b25urnd0dLSn04FlBplFZtKALM/o+GQbt1wu11dXV78VCoXfnQ5cKpWqJycnd4uLi33Dw8NPx8bGenZ3d381Gg13gNvh5vP5yDxG6fLysqIF2Thw1HG1IRsFdgVXE7IxYNdwtSAbAXYV9yHkVCrVvbe3ZwQ5dODWuXBhYeG1i7jtkEdGRp6ZQg4VuIWbyWScxrWJHBowuDqQQwEGVw9y4MDg6kIOFBhcfciBX2zwPI/fLlS0doG+gmXnySFeDvNy3pOdKTtUdip895ufn3/u+34qmUx6+/v7P5v/8c7kudmq34NB1oMb2qdokHXghnoOBtk+bqjAINvHDR0YZLu4RoBBtodrDNhlZJu4RoFdRLaNaxzYJWQNuFaAXUDWgmsN+CHkfD5/K98r7mTc8fHxZDabHdOAaxX4f+SLi4s/m5ubP2x9QTyobm5uak3knmKxWLaNK8lvF361vSit2ztsL0YU51Hx24VRgdU4D/cHRzyAASaACWACmAAmgAlggAlgApgAJoDpUf0TYADnqrAanCLw9QAAAABJRU5ErkJggg==) } From 692b1cb922abc04101706ef3db787ef1c7c7071e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 17:37:05 +0100 Subject: [PATCH 37/85] 404 response for replica operation sent to master now causes re-send; always send in background thread --- .../replication/OperationsToMasterSender.java | 2 +- .../impl/UnsentOperationsSenderJob.java | 23 +++---------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java index 85cd8f3dd3c..685f9ae47dd 100755 --- a/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java +++ b/java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationsToMasterSender.java @@ -58,7 +58,7 @@ public interface OperationsToMasterSender dos.writeUTF(getId().toString()); this.writeOperation(operationWithResultWithIdWrapper, outputStream, /* closeStream */ true); final int responseCode = connection.getResponseCode(); - if (responseCode < 300 || responseCode >= 500) { + if (responseCode < 300 || responseCode >= 500 || responseCode == 404) { // if OK or an internal error, process as usual; in case of >= 500 this will throw an exception BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream())); bufferedReader.close(); diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java index fe9d80dc020..06c5713406a 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/UnsentOperationsSenderJob.java @@ -55,28 +55,11 @@ public class UnsentOperationsSenderJob implements OperationsToMasterSendingQueue } @Override - public , T> void scheduleForSending( + public synchronized , T> void scheduleForSending( OperationWithResult operationWithResult, OperationsToMasterSender sender) { - final boolean runSynchronously; - synchronized (this) { - queue.addLast(new Pair<>(operationWithResult, sender)); - runSynchronously = !scheduled; - if (!scheduled) { - // try to send immediately; setting scheduled to true will cause subsequent operation - // submissions to be picked up by that run() call as long as they arrive before the run() - // method resets scheduled to false and returns. - scheduled = true; - } - } - // This way, calling the run() method happens outside of the synchronized block above, and - // so other threads may insert operations into the queue while the run() method is trying - // to send, which will then be picked up by any - // synchronous call to run() triggered in the next block: - if (runSynchronously) { - // In case of a send error, the run() method will itself re-schedule - run(); - } + queue.addLast(new Pair<>(operationWithResult, sender)); + ensureScheduled(); } /** From 1e9dffb35e193885be8c85bd7d3d5594802cc043 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 5 Feb 2020 17:39:48 +0100 Subject: [PATCH 38/85] added release notes for latest replication improvements --- java/com.sap.sailing.www/release_notes_admin.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 4ebc91df21d..9806b6c4ccf 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -35,6 +35,14 @@ will consider, as in
                                     ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Drepliate.restore.replicas=true"
                                   
      +
    • Replication from replica to master will now re-send upon HTTP status 404 which + may indicate a re-starting master where the health check hasn't properly disabled + forwarding requests to the instance. +
    • +
    • Replication operations that a replica needs to send back to the master instance are + now consistently sent in a background task, avoiding any hold-ups and latencies in + client / user interaction. +

    January 2020

      From 85533f822933b56ec77bb9a5aef739b111541c71 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 6 Feb 2020 08:42:29 +0100 Subject: [PATCH 39/85] fixed admin release notes regarding system property name for cookie sharing --- java/com.sap.sailing.www/release_notes_admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 9806b6c4ccf..5fd30f111a4 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -69,7 +69,7 @@

    November 2019

      -
    • Added system property security.sharedAcrossSubdomains which can be used to +
    • Added system property security.sharedAcrossSubdomainsOf which can be used to configure the security service as shared across several subdomains of the parent domain specified by the system property. Use, e.g., as in
                                     -Dsecurity.sharedAcrossSubdomainsOf=sapsailing.com
      
      From dbf6b2229b651aa72216496a338f6c22caa8a988 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Thu, 6 Feb 2020 08:42:57 +0100
      Subject: [PATCH 40/85] handle / characters in compareServers, escaping them
       with %2F
      
      ---
       java/target/compareServers | 4 ++--
       1 file changed, 2 insertions(+), 2 deletions(-)
      
      diff --git a/java/target/compareServers b/java/target/compareServers
      index 6fa8d9f48f1..4832de3e11e 100755
      --- a/java/target/compareServers
      +++ b/java/target/compareServers
      @@ -35,8 +35,8 @@ NEWGROUPS_FOR_CONTINUE=${NEWGROUPS}.continue
       
       # If we continue, don't download again
       if [ "$continue" != "1" ]; then
      -  wget --no-cache $WGET_QUIET -O - ${OLDSERVER}/sailingserver/api/v1/leaderboardgroups | sed -e 's/\",\"/\n/g' -e 's/\[/\n/' -e 's/\]/\n/' -e 's/\"//g' | grep -v "^$" | sort >$OLDGROUPS
      -  wget --no-cache $WGET_QUIET -O - ${NEWSERVER}/sailingserver/api/v1/leaderboardgroups | sed -e 's/\",\"/\n/g' -e 's/\[/\n/' -e 's/\]/\n/' -e 's/\"//g' | grep -v "^$" | sort >$NEWGROUPS
      +  wget --no-cache $WGET_QUIET -O - ${OLDSERVER}/sailingserver/api/v1/leaderboardgroups | sed -e 's/\",\"/\n/g' -e 's/\[/\n/' -e 's/\]/\n/' -e 's/\"//g' -e 's/\//%2F/g' | grep -v "^$" | sort >$OLDGROUPS
      +  wget --no-cache $WGET_QUIET -O - ${NEWSERVER}/sailingserver/api/v1/leaderboardgroups | sed -e 's/\",\"/\n/g' -e 's/\[/\n/' -e 's/\]/\n/' -e 's/\"//g' -e 's/\//%2F/g' | grep -v "^$" | sort >$NEWGROUPS
       fi
       
       download_and_fix_boatclasses () {
      
      From 91033f1b25977d55c9c4c1161d6a801bcf2ddc91 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Thu, 6 Feb 2020 08:43:26 +0100
      Subject: [PATCH 41/85] improved logging for initial load in
       SecurityServiceImpl
      
      ---
       java/com.sap.sailing.server/SailingServer (No Proxy).launch   | 4 ++--
       .../src/com/sap/sse/security/impl/SecurityServiceImpl.java    | 4 +++-
       2 files changed, 5 insertions(+), 3 deletions(-)
      
      diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch
      index 2ff74ea126d..6c8d1a14bc2 100755
      --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch	
      +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch	
      @@ -32,9 +32,9 @@
       
       
       
      -
      +
       
       
       
      -
      +
       
      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 94e83de6874..e83aae93808 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
      @@ -2105,8 +2105,10 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
               }
               logger.info("Reading isSharedAcrossSubdomains...");
               sharedAcrossSubdomainsOf = (String) is.readObject();
      -        baseUrlForCrossDomainStorage = (String) is.readObject();
               logger.info("...as "+sharedAcrossSubdomainsOf);
      +        logger.info("Reading baseUrlForCrossDomainStorage...");
      +        baseUrlForCrossDomainStorage = (String) is.readObject();
      +        logger.info("...as "+baseUrlForCrossDomainStorage);
               logger.info("Done filling SecurityService");
           }
       
      
      From 0333c2fd010b1555563b960f4b4c32e7f9b831f5 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Thu, 6 Feb 2020 14:08:55 +0100
      Subject: [PATCH 42/85] bug5160: added optional course geometry to the
       serialization output of RegattasResource
      
      ---
       .../impl/CourseBaseJsonSerializer.java        | 18 ++--
       .../jaxrs/AbstractSailingServerResource.java  | 20 +++-
       .../gateway/jaxrs/api/MarkResource.java       | 47 +++++-----
       .../gateway/jaxrs/api/RegattasResource.java   | 91 ++++++++++++++++---
       4 files changed, 126 insertions(+), 50 deletions(-)
      
      diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseJsonSerializer.java
      index 126ec044fdb..56c48ad6d0e 100644
      --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseJsonSerializer.java
      +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseJsonSerializer.java
      @@ -19,21 +19,21 @@ public class CourseBaseJsonSerializer implements JsonSerializer {
           }
       
           @Override
      -    public JSONObject serialize(CourseBase object) {
      +    public JSONObject serialize(CourseBase course) {
               JSONObject result = new JSONObject();
      -
      -        result.put(FIELD_NAME, object.getName());
      -        if (object.getOriginatingCourseTemplateIdOrNull() != null) {
      -            result.put(FIELD_ORIGINATING_COURSE_TEMPLATE_ID, object.getOriginatingCourseTemplateIdOrNull());
      +        result.put(FIELD_NAME, course.getName());
      +        if (course.getOriginatingCourseTemplateIdOrNull() != null) {
      +            result.put(FIELD_ORIGINATING_COURSE_TEMPLATE_ID, course.getOriginatingCourseTemplateIdOrNull());
               }
      -
               JSONArray waypoints = new JSONArray();
      -        for (Waypoint waypoint : object.getWaypoints()) {
      -            waypoints.add(waypointSerializer.serialize(waypoint));
      +        for (Waypoint waypoint : course.getWaypoints()) {
      +            waypoints.add(serializeWaypoint(waypoint));
               }
               result.put(FIELD_WAYPOINTS, waypoints);
      -
               return result;
           }
       
      +    protected JSONObject serializeWaypoint(Waypoint waypoint) {
      +        return waypointSerializer.serialize(waypoint);
      +    }
       }
      diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
      index 103de1a4e61..40315aada70 100755
      --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
      +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
      @@ -132,13 +132,27 @@ public abstract class AbstractSailingServerResource {
       
           protected TrackedRace findTrackedRace(String regattaName, String raceName) {
               Regatta regatta = findRegattaByName(regattaName);
      -        RaceDefinition race = findRaceByName(regatta, raceName);
      -        TrackedRace trackedRace = null;
      -        if (regatta != null && race != null) {
      +        final TrackedRace trackedRace;
      +        if (regatta == null) {
      +            trackedRace = null;
      +        } else {
      +            trackedRace = findTrackedRace(regatta, raceName);
      +        }
      +        return trackedRace;
      +    }
      +
      +    protected TrackedRace findTrackedRace(Regatta regatta, String raceName) {
      +        final TrackedRace trackedRace;
      +        final RaceDefinition race = findRaceByName(regatta, raceName);
      +        if (race != null) {
                   DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta);
                   if (trackedRegatta != null) {
                       trackedRace = trackedRegatta.getExistingTrackedRace(race);
      +            } else {
      +                trackedRace = null;
                   }
      +        } else {
      +            trackedRace = null;
               }
               return trackedRace;
           }
      diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkResource.java
      index 55347a8c29c..cf6bc3e3c07 100644
      --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkResource.java
      +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/MarkResource.java
      @@ -214,17 +214,17 @@ public class MarkResource extends AbstractSailingServerResource {
           @Produces("application/json;charset=UTF-8")
           public Response addCourseDefinitionToRaceLog(String json)
                   throws DoesNotHaveRegattaLogException, NotFoundException, ParseException, JsonDeserializationException {
      -        Object requestBody = JSONValue.parseWithException(json);
      -        JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
      -        String leaderboardName = (String) requestObject.get(LEADERBOARD_NAME);
      -        String originatingCourseTemplateIdAsString = (String) requestObject.get(ORIGINATING_COURSE_TEMPLATE_ID);
      -        UUID originatingCourseTemplateId = originatingCourseTemplateIdAsString != null ? UUID.fromString(originatingCourseTemplateIdAsString) : null;
      +        final Object requestBody = JSONValue.parseWithException(json);
      +        final JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
      +        final String leaderboardName = (String) requestObject.get(LEADERBOARD_NAME);
      +        final String originatingCourseTemplateIdAsString = (String) requestObject.get(ORIGINATING_COURSE_TEMPLATE_ID);
      +        final UUID originatingCourseTemplateId = originatingCourseTemplateIdAsString != null ? UUID.fromString(originatingCourseTemplateIdAsString) : null;
               SecurityUtils.getSubject().checkPermission(
                       SecuredDomainType.LEADERBOARD.getStringPermissionForTypeRelativeIdentifier(DefaultActions.UPDATE,
                               Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName)));
      -        String raceColumnName = (String) requestObject.get(RACE_COLUMN_NAME);
      -        String fleetName = (String) requestObject.get(FLEET_NAME);
      -        RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
      +        final String raceColumnName = (String) requestObject.get(RACE_COLUMN_NAME);
      +        final String fleetName = (String) requestObject.get(FLEET_NAME);
      +        final RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
               String courseName = "Course of " + raceColumnName;
               if (!LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName)) {
                   courseName += " - " + fleetName;
      @@ -234,25 +234,25 @@ public class MarkResource extends AbstractSailingServerResource {
               if (lastPublishedCourse == null) {
                   lastPublishedCourse = new CourseDataImpl(courseName);
               }
      -        JSONArray controlPointsRaw = (JSONArray) requestObject.get("controlPoints");
      -        List> controlPoints = new ArrayList<>();
      +        final JSONArray controlPointsRaw = (JSONArray) requestObject.get("controlPoints");
      +        final List> controlPoints = new ArrayList<>();
               for (Object controlPointUnsafe : controlPointsRaw) {
                   JSONObject controlPointRaw = Helpers.toJSONObjectSafe(controlPointUnsafe);
                   final String passingInstructionString = (String) controlPointRaw.get("passingInstruction");
      -            PassingInstruction passing = PassingInstruction.valueOfIgnoringCase(passingInstructionString);
      -            JSONArray marksRaw = (JSONArray) controlPointRaw.get("marks");
      +            final PassingInstruction passing = PassingInstruction.valueOfIgnoringCase(passingInstructionString);
      +            final JSONArray marksRaw = (JSONArray) controlPointRaw.get("marks");
                   if (marksRaw.size() == 1) {
      -                String markName = (String) marksRaw.get(0);
      -                Mark mark = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markName);
      +                final String markName = (String) marksRaw.get(0);
      +                final Mark mark = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markName);
                       if (mark == null) {
                           throw new IllegalStateException("Could not resolve mark " + markName);
                       }
                       controlPoints.add(new Pair<>(mark, passing));
                   } else {
      -                String markNameA = (String) marksRaw.get(0);
      -                String markNameB = (String) marksRaw.get(1);
      -                Mark markA = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markNameA);
      -                Mark markB = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markNameB);
      +                final String markNameA = (String) marksRaw.get(0);
      +                final String markNameB = (String) marksRaw.get(1);
      +                final Mark markA = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markNameA);
      +                final Mark markB = getService().getBaseDomainFactory().getExistingMarkByIdAsString(markNameB);
                       if (markA == null) {
                           throw new IllegalStateException("Could not resolve mark " + markA);
                       }
      @@ -263,25 +263,24 @@ public class MarkResource extends AbstractSailingServerResource {
                       if (controlPointName == null || controlPointName.isEmpty()) {
                           controlPointName = markA.getName() + "-" + markB.getName();
                       }
      -
      -                String shortName = (String) controlPointRaw.get(CONTROL_POINT_SHORT_NAME);
      +                final String shortName = (String) controlPointRaw.get(CONTROL_POINT_SHORT_NAME);
                       controlPoints.add(new Pair<>(new ControlPointWithTwoMarksImpl(markA, markB, controlPointName,
                               shortName == null ? controlPointName : shortName), passing));
                   }
               }
      -        Course course = new CourseImpl(courseName, lastPublishedCourse.getWaypoints(), originatingCourseTemplateId);
      +        final Course course = new CourseImpl(courseName, lastPublishedCourse.getWaypoints(), originatingCourseTemplateId);
               try {
                   course.update(controlPoints, lastPublishedCourse.getAssociatedRoles(),
                           lastPublishedCourse.getOriginatingCourseTemplateIdOrNull(), getService().getBaseDomainFactory());
               } catch (Exception e) {
                   throw new RuntimeException(e);
               }
      -        RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(),
      +        final RaceLogEvent event = new RaceLogCourseDesignChangedEventImpl(MillisecondsTimePoint.now(),
                       getService().getServerAuthor(), raceLog.getCurrentPassId(), course, CourseDesignerMode.ADMIN_CONSOLE);
               raceLog.add(event);
      -        CourseBase updatedPublishedCourse = new LastPublishedCourseDesignFinder(raceLog,
      +        final CourseBase updatedPublishedCourse = new LastPublishedCourseDesignFinder(raceLog,
                       /* onlyCoursesWithValidWaypointList */ false).analyze();
      -        JSONObject jsonResult = new JSONObject();
      +        final JSONObject jsonResult = new JSONObject();
               jsonResult.put("course",
                       new CourseJsonSerializer(new CourseBaseJsonSerializer(
                               new WaypointJsonSerializer(new ControlPointJsonSerializer(new MarkJsonSerializer(),
      diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      index ab87e200a42..f6387974a15 100644
      --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      @@ -55,6 +55,7 @@ import com.sap.sailing.domain.base.CompetitorWithBoat;
       import com.sap.sailing.domain.base.Course;
       import com.sap.sailing.domain.base.CourseBase;
       import com.sap.sailing.domain.base.Fleet;
      +import com.sap.sailing.domain.base.Leg;
       import com.sap.sailing.domain.base.Mark;
       import com.sap.sailing.domain.base.RaceColumn;
       import com.sap.sailing.domain.base.RaceColumnInSeries;
      @@ -88,6 +89,7 @@ import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
       import com.sap.sailing.domain.tracking.DynamicTrackedRace;
       import com.sap.sailing.domain.tracking.GPSFixTrack;
       import com.sap.sailing.domain.tracking.Maneuver;
      +import com.sap.sailing.domain.tracking.MarkPassing;
       import com.sap.sailing.domain.tracking.RaceWindCalculator;
       import com.sap.sailing.domain.tracking.TrackedLeg;
       import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
      @@ -100,6 +102,8 @@ import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
       import com.sap.sailing.server.gateway.serialization.JsonSerializer;
       import com.sap.sailing.server.gateway.serialization.coursedata.impl.ControlPointJsonSerializer;
       import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseJsonSerializer;
      +import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseWithGeometryJsonSerializer;
      +import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseWithGeometryJsonSerializer.CourseGeometry;
       import com.sap.sailing.server.gateway.serialization.coursedata.impl.GateJsonSerializer;
       import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer;
       import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer;
      @@ -141,6 +145,7 @@ import com.sap.sailing.server.operationaltransformation.AddColumnToSeries;
       import com.sap.sailing.server.operationaltransformation.UpdateSeries;
       import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
       import com.sap.sse.InvalidDateException;
      +import com.sap.sse.common.Bearing;
       import com.sap.sse.common.Color;
       import com.sap.sse.common.Distance;
       import com.sap.sse.common.Duration;
      @@ -1061,12 +1066,12 @@ public class RegattasResource extends AbstractSailingServerResource {
                   response = getBadRegattaErrorResponse(regattaName);
               } else {
                   getSecurityService().checkCurrentUserReadPermission(regatta);
      -            RaceDefinition race = findRaceByName(regatta, raceName);
      -            if (race == null) {
      +            TrackedRace trackedRace = findTrackedRace(regatta, raceName);
      +            if (trackedRace == null) {
                       response = getBadRaceErrorResponse(regattaName, raceName);
                   } else {
      -                CourseBase course = race.getCourse();
      -                response = getCourseResult(course);
      +                CourseBase course = trackedRace.getRace().getCourse();
      +                response = getCourseResult(course, trackedRace);
                   }
               }
               return response;
      @@ -1089,14 +1094,13 @@ public class RegattasResource extends AbstractSailingServerResource {
                   response = getBadRegattaErrorResponse(regattaName);
               } else {
                   getSecurityService().checkCurrentUserReadPermission(regatta);
      -            
                   final RaceColumn raceColumn = findRaceColumnByName(regatta, raceColumnName);
                   final Fleet fleet = findFleetByName(raceColumn, fleetName);
      -
                   if (raceColumn == null || fleet == null) {
                       response = getBadRaceErrorResponse(regattaName, raceColumnName, fleetName);
                   } else {
      -                final RaceDefinition raceDefinition = raceColumn.getRaceDefinition(fleet);
      +                final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
      +                final RaceDefinition raceDefinition = trackedRace == null ? null : trackedRace.getRace();
                       final CourseBase course;
                       if (raceDefinition != null) {
                           course = raceDefinition.getCourse();
      @@ -1105,29 +1109,88 @@ public class RegattasResource extends AbstractSailingServerResource {
                                   raceColumn.getRaceLog(fleet), /* onlyCoursesWithValidWaypointList */ true);
                           course = courseDesginFinder.analyze();
                       }
      -                
                       if (course == null) {
                           response = Response.status(Status.NOT_FOUND).entity("No course found for given race.").build();
                       } else {
      -                    response = getCourseResult(course);
      +                    response = getCourseResult(course, trackedRace);
                       }
                   }
               }
               return response;
           }
       
      -    private Response getCourseResult(CourseBase course) {
      +    /**
      +     * @param optionalTrackedRace
      +     *            if not {@code null}, the tracked race will be used to obtain information about the course geometry
      +     *            which the serializer will put into the result.
      +     */
      +    private Response getCourseResult(CourseBase course, TrackedRace optionalTrackedRace) {
               Response response;
      -        CourseBaseJsonSerializer serializer = new CourseBaseJsonSerializer(new WaypointJsonSerializer(
      +        final WaypointJsonSerializer waypointSerializer = new WaypointJsonSerializer(
                       new ControlPointJsonSerializer(new MarkJsonSerializer(), new GateJsonSerializer(
      -                        new MarkJsonSerializer()))));
      -
      -        JSONObject jsonCourse = serializer.serialize(course);
      +                        new MarkJsonSerializer())));
      +        final JSONObject jsonCourse;
      +        if (optionalTrackedRace == null) {
      +            jsonCourse = new CourseBaseJsonSerializer(waypointSerializer).serialize(course);
      +        } else {
      +            final CourseGeometry geometry = getCourseGeometry(optionalTrackedRace);
      +            jsonCourse = new CourseBaseWithGeometryJsonSerializer(waypointSerializer).serialize(new Pair<>(course, geometry));
      +        }
               String json = jsonCourse.toJSONString();
               response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
               return response;
           }
           
      +    /**
      +     * The leg distance and bearing is taken for the time point when the first boat enters the leg, or for the start of the race
      +     * if no mark passing exists for that leg yet, or for the start of tracking if no start of race exists, or for "now" if no
      +     * start of tracking time point exists, either. The total distance is computed as the sum of the leg distances, therefore
      +     * not necessarily representing the total course distance at any single point in time.
      +     */
      +    private CourseGeometry getCourseGeometry(TrackedRace trackedRace) {
      +        assert trackedRace != null;
      +        final Course course = trackedRace.getRace().getCourse();
      +        Distance totalDistance = Distance.NULL;
      +        final Map legDistances = new HashMap<>();
      +        final Map legBearings = new HashMap<>();
      +        course.lockForRead();
      +        try {
      +            for (final Leg leg : course.getLegs()) {
      +                final TrackedLeg trackedLeg = trackedRace.getTrackedLeg(leg);
      +                final TimePoint timePointForLegGeometry = getTimePointForLegGeometry(trackedRace, leg);
      +                final Distance legDistance = trackedLeg.getGreatCircleDistance(timePointForLegGeometry);
      +                legDistances.put(leg, legDistance);
      +                legBearings.put(leg, trackedLeg.getLegBearing(timePointForLegGeometry));
      +                totalDistance = totalDistance.add(legDistance);
      +            }
      +        } finally {
      +            course.unlockAfterRead();
      +        }
      +        return new CourseGeometry(totalDistance, legDistances, legBearings);
      +    }
      +
      +    private TimePoint getTimePointForLegGeometry(TrackedRace trackedRace, Leg leg) {
      +        final Iterable markPassingsForLegStart = trackedRace.getMarkPassingsInOrder(leg.getFrom());
      +        final Iterator firstMarkPassingForLegStartIter = markPassingsForLegStart.iterator();
      +        final TimePoint result;
      +        if (firstMarkPassingForLegStartIter.hasNext()) {
      +            result = firstMarkPassingForLegStartIter.next().getTimePoint();
      +        } else {
      +            final TimePoint raceStartTime = trackedRace.getStartOfRace();
      +            if (raceStartTime != null) {
      +                result = raceStartTime;
      +            } else {
      +                final TimePoint startOfTracking = trackedRace.getStartOfTracking();
      +                if (startOfTracking != null) {
      +                    result = startOfTracking;
      +                } else {
      +                    result = MillisecondsTimePoint.now();
      +                }
      +            }
      +        }
      +        return result;
      +    }
      +
           /**
            * Gets the target time of the race
            * 
      
      From 5e12bb2c818259c0354c723dd38df57b3b52990e Mon Sep 17 00:00:00 2001
      From: "service.tip.git" 
      Date: Thu, 6 Feb 2020 18:48:20 +0000
      Subject: [PATCH 43/85] [INTERNAL] Translation delivery: commit by LX Lab
      
      Change-Id: I65845b06da46d126ad313ed7a5c8387f8002ba39
      ---
       .../ui/client/StringMessages_es.properties    | 41 ++++++++++++++++--
       .../ui/client/StringMessages_fr.properties    | 41 ++++++++++++++++--
       .../ui/client/StringMessages_it.properties    | 41 ++++++++++++++++--
       .../ui/client/StringMessages_ja.properties    | 41 ++++++++++++++++--
       .../ui/client/StringMessages_pt.properties    | 41 ++++++++++++++++--
       .../ui/client/StringMessages_ru.properties    | 43 ++++++++++++++++---
       .../ui/client/StringMessages_zh.properties    | 41 ++++++++++++++++--
       .../StringMessages_es.properties              |  2 +
       .../StringMessages_fr.properties              |  2 +
       .../StringMessages_it.properties              |  2 +
       .../StringMessages_ja.properties              |  2 +
       .../StringMessages_pt.properties              |  2 +
       .../StringMessages_ru.properties              |  2 +
       .../StringMessages_zh.properties              |  2 +
       .../client/i18n/StringMessages_es.properties  |  1 +
       .../client/i18n/StringMessages_fr.properties  |  1 +
       .../client/i18n/StringMessages_it.properties  |  1 +
       .../client/i18n/StringMessages_ja.properties  |  1 +
       .../client/i18n/StringMessages_pt.properties  |  1 +
       .../client/i18n/StringMessages_ru.properties  |  1 +
       .../client/i18n/StringMessages_zh.properties  |  1 +
       21 files changed, 281 insertions(+), 29 deletions(-)
      
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties
      index b973b978e8c..00cbbb25a84 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=Indique un URL.
       pleaseEnterNonEmptyUrlOrUploadImage=Indique un URL o cargue una imagen.
       addResultImportUrl=Añadir un URL de importación de resultados
       errorAddingResultImportUrl=Error al añadir URL de importación de resultados: {0}
      +eventIdOrUrl=ID de evento o URL
      +doYouReallyWantToRemoveResultImportUrls=¿Desea realmente eliminar los URLs de importación de resultados seleccionados?
       remoteServerInstances=Instancias de servidor remoto
       localServer=Servidor local
       errorRefreshingSailingServers=Error al refrescar instancias SailingServer: {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=Ir a la versión de escritorio
       goBack=Volver
       showAllX=Mostrar todo {0}
       collapseX=Comprimir {0}
      +
       numberOfGPSFixes=Correcciones de GPS
       fastestSailor=Más rápido
       highestSpeed=Máxima velocidad
      @@ -2242,18 +2245,18 @@ invalidSecret=Secreto no válido
       warningSailInsightVersion=El servidor se ha configurado para el uso con SAP Sail Insight 1. Las regatas abiertas solo son compatibles con SAP Sail Insight 2; se pueden producir problemas de uso.
       invalidState=Estado no válido
       doYouReallyWantToRemoveDeviceConfiguration=¿Desea realmente eliminar la configuración del dispositivo {0}?
      -errorFetchingSimulationData=Se ha producido un error al obtener los datos de simulación: {0}
      +errorFetchingSimulationData=Se ha producido un error al obtener los datos de simulación: {0}. Asegúrese de haber iniciado sesión con su cuenta de usuario.
       usernameAndPasswordMustBothBeSet=El nombre de usuario y contraseña deben estar completados o en blanco
       explainUserName=El nombre de usuario a utilizar para la autenticación en el maestro
       explainPassword=La contraseña a utilizar para la autenticación en el maestro
      +tracTracConnection=Conexión TracTrac
       addTracTracConnection=Añadir conexión TracTrac
      -editTracTracConnection=Tratar conexión TracTrac
       filterTracTracConnections=Filtrar conexiones TracTrac
       creatorName=Nombre de creador
      +swissTimingAchivedConnection=Conexión SwissTimingArchived
       addSwissTimingAchivedConnection=Añadir conexión SwissTimingArchived
      -editSwissTimingAchivedConnection=Tratar conexión SwissTimingArchived
       filterSwissTimingAchivedConnections=Filtrar conexiones SwissTimingArchived
      -editSwissTimingConnections=Tratar conexión SwissTiming
      +swissTimingConnections=Conexión SwissTiming
       filterSwissTimingConnections=Filtrar conexiones SwissTiming
       addSwissTimingConnection=Añadir conexión SwissTiming
       updateURL=Actualizar URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=Regata de barlovent
       errorWhileLoadingUserSettings=Error al cargar las opciones de usuario: {0}
       certificates=Certificados
       dataBy=Datos por
      +currentPositionLatitudeTooltip=Latitud de posición en grados (valores positivos denotan hemisferio norte; valores negativos, hemisferio sur)
      +currentPositionLongitudeTooltip=Longitud de posición en grados (valores positivos denotan este; valores negativos, oeste)
      +courseCreation=Creación de rumbo
      +filterMarkTemplateByName=Filtrar plantillas de marcas
      +markTemplates=Plantillas de marcas
      +filterMarkPropertiesByName=Filtrar propiedades de marcas
      +markProperties=Propiedades de marcas
      +filterCourseTemplateByName=Filtrar plantillas de rumbos
      +courseTemplates=Plantillas de rumbos
      +markTemplate=Plantilla de marcas
      +markRole1=Rol de marca 1
      +markRole2=Rol de marca 2
      +pleaseSelectAValidMarkTemplate=Seleccione una plantilla de marcas válida
      +pleaseSelectAValidPassingInstruction=Seleccione una instrucción de paso válida
      +doYouReallyWantToRemoveCourseTemplate=¿Desea realmente eliminar la plantilla de rumbos "{0}"? Todos los rumbos asociados se desvincularán.
      +couldNotRemoveCourseTemplate=No se ha podido eliminar la plantilla de rumbos "{0}".
      +doYouReallyWantToRemoveMarkProperties=¿Desea realmente eliminar las propiedades de marcas "{0}"?
      +couldNotRemoveMarkProperties=No se han podido eliminar las propiedades de marcas "{0}".
      +markRoles=Roles de marca
      +filterMarkRoles=Filtrar roles de marca
      +wayPointRequiresPassingInstruction=Seleccione una instrucción de paso válida para el punto de ruta
      +wayPointRequiresShortName=Introduzca un nombre breve del punto de ruta
      +wayPointRequiresName=Introduzca un nombre del punto de ruta
      +wayPointMarkTemplatesAreTheSame=Las plantillas de marcas utilizadas en un punto de ruta no pueden ser iguales
      +wayPointMarkInSequenceMissing=La marca utilizada en la secuencia no existe en la plantilla de rumbos
      +defaultNumberOfLaps=# vueltas por defecto
      +invalidImageURL=URL de imagen no válido
      +setDeviceIdentifier=Fijar identificador de dispositivo
      +setPosition=Fijar posición
      +spareMarksAndTheirOptionalDefaultMarkRoles=Marcas adicionales y sus roles de marca por defecto opcionales
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties
      index e3ba6af1eeb..f01f5b6df56 100755
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=Saisissez une URL.
       pleaseEnterNonEmptyUrlOrUploadImage=Veuillez saisir une URL ou charger une image.
       addResultImportUrl=Ajouter URL d''importation des résultats
       errorAddingResultImportUrl=Erreur lors de l''ajout de l''URL d''importation des résultats : {0}
      +eventIdOrUrl=URL ou ID de manifestation
      +doYouReallyWantToRemoveResultImportUrls=Voulez-vous vraiment supprimer les URL d''importation de résultats sélectionnées ?
       remoteServerInstances=Instances de serveur distant
       localServer=Serveur local
       errorRefreshingSailingServers=Erreur lors de la mise à jour des instances SailingServer : {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=Aller à la version bureau
       goBack=Retour
       showAllX=Afficher tout {0}
       collapseX=Réduire {0}
      +
       numberOfGPSFixes=Positions GPS
       fastestSailor=Le plus rapide
       highestSpeed=Vitesse maximale
      @@ -2242,18 +2245,18 @@ invalidSecret=Secret non valide
       warningSailInsightVersion=Le serveur est configuré pour être utilisé avec SAP Sailing Boat Tracker 1. Les régates ouvertes sont prises en charge uniquement dans SAP Sailing Boat Tracker 2. Cela peut provoquer des problèmes d''utilisation.
       invalidState=Statut non valide
       doYouReallyWantToRemoveDeviceConfiguration=Voulez-vous vraiment supprimer la configuration de l''appareil "{0}" ?
      -errorFetchingSimulationData=Une erreur s''est produite lors de l''accès aux données de simulation : {0}
      +errorFetchingSimulationData=Une erreur s''est produite lors de l''accès aux données de simulation : {0}. Assurez-vous que vous êtes bien connecté avec votre compte utilisateur.
       usernameAndPasswordMustBothBeSet=Le nom d''utilisateur et le mot de passe doivent tous les deux être renseignés ou vides.
       explainUserName=Nom d''utilisateur à utiliser pour l''authentification dans le système principal
       explainPassword=Mot de passe à utiliser pour l''authentification dans le système principal
      +tracTracConnection=Connexion TracTrac
       addTracTracConnection=Ajouter connexion TracTrac
      -editTracTracConnection=Modifier connexion TracTrac
       filterTracTracConnections=Filtrer connexions TracTrac
       creatorName=Nom de l''auteur
      +swissTimingAchivedConnection=Connexion SwissTimingArchived
       addSwissTimingAchivedConnection=Ajouter connexion SwissTimingArchived
      -editSwissTimingAchivedConnection=Modifier connexion SwissTimingArchived
       filterSwissTimingAchivedConnections=Filtrer connexions SwissTimingArchived
      -editSwissTimingConnections=Modifier connexion SwissTiming
      +swissTimingConnections=Connexion SwissTiming
       filterSwissTimingConnections=Filtrer connexions SwissTiming
       addSwissTimingConnection=Ajouter connexion SwissTiming
       updateURL=Mettre à jour URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive="Windward/Leeward"
       errorWhileLoadingUserSettings=Erreur lors du chargement des options utilisateur : {0}
       certificates=Certificats
       dataBy=Données par
      +currentPositionLatitudeTooltip=Latitude de la position en degrés (les valeurs positives indiquent l''hémisphère nord, les valeurs négatives indiquent l''hémisphère sud)
      +currentPositionLongitudeTooltip=Longitude de la position en degrés (les valeurs positives indiquent l''est, les valeurs négatives indiquent l''ouest)
      +courseCreation=Création de parcours
      +filterMarkTemplateByName=Filtrer les modèles de marque
      +markTemplates=Modèles de marque
      +filterMarkPropertiesByName=Filtrer les propriétés de marque
      +markProperties=Propriétés de marque
      +filterCourseTemplateByName=Filtrer les modèles de parcours
      +courseTemplates=Modèles de parcours
      +markTemplate=Modèle de marque
      +markRole1=Rôle de marque 1
      +markRole2=Rôle de marque 2
      +pleaseSelectAValidMarkTemplate=Veuillez sélectionner un modèle de marque valide.
      +pleaseSelectAValidPassingInstruction=Veuillez sélectionner une instruction de passage valide.
      +doYouReallyWantToRemoveCourseTemplate=Voulez-vous vraiment supprimer le modèle de parcours "{0}" ? Tous les parcours associés ne seront plus liés.
      +couldNotRemoveCourseTemplate=Impossible de supprimer le modèle de parcours "{0}".
      +doYouReallyWantToRemoveMarkProperties=Voulez-vous vraiment supprimer les propriétés de marque "{0}" ?
      +couldNotRemoveMarkProperties=Impossible de supprimer les propriétés de marque "{0}".
      +markRoles=Rôles de marque
      +filterMarkRoles=Filtrer les rôles de marque
      +wayPointRequiresPassingInstruction=Veuillez sélectionner une instruction de passage valide pour le point de cheminement.
      +wayPointRequiresShortName=Veuillez saisir un nom court pour le point de cheminement.
      +wayPointRequiresName=Veuillez saisir un nom pour le point de cheminement.
      +wayPointMarkTemplatesAreTheSame=Les modèles de marque utilisés dans un point de cheminement ne peuvent pas être les mêmes.
      +wayPointMarkInSequenceMissing=La marque utilisée dans la séquence n''existe pas dans le modèle de parcours.
      +defaultNumberOfLaps=Nombre de tours par défaut
      +invalidImageURL=URL de l''image non valide
      +setDeviceIdentifier=Définir l''identificateur d''appareil
      +setPosition=Définir la position
      +spareMarksAndTheirOptionalDefaultMarkRoles=Marques de rechange et leur rôles de marque par défaut facultatifs
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties
      index d74d69e8f78..224aa8f2063 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=Inserire un URL.
       pleaseEnterNonEmptyUrlOrUploadImage=Inserire un URL o caricare un''immagine.
       addResultImportUrl=Aggiungi URL di importazione dei risultati
       errorAddingResultImportUrl=Errore nell’aggiunta degli URL di importazione dei risultati: {0}
      +eventIdOrUrl=URL o ID evento
      +doYouReallyWantToRemoveResultImportUrls=Eliminare gli URL di importazione dei risultati selezionati?
       remoteServerInstances=Istanze del server remoto
       localServer=Server locale
       errorRefreshingSailingServers=Errore nell’aggiornamento delle istanze SailingServer: {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=Vai a versione desktop
       goBack=Indietro
       showAllX=Mostra tutto {0}
       collapseX=Comprimi {0}
      +
       numberOfGPSFixes=Punti nave GPS
       fastestSailor=Più veloce
       highestSpeed=Velocità massima
      @@ -2242,18 +2245,18 @@ invalidSecret=Stringa segreta non valida
       warningSailInsightVersion=Il server è configurato per l''uso con SAP Sail Insight 1. Le regate aperte sono supportate solo in SAP Sail Insight 2. Ciò può causare problemi di utilizzo
       invalidState=Stato non valido
       doYouReallyWantToRemoveDeviceConfiguration=Confermare l''eliminazione della configurazione del dispositivo {0}?
      -errorFetchingSimulationData=Si è verificato un errore nella raccolta dei dati di simulazione: {0}
      +errorFetchingSimulationData=Si è verificato un errore nella raccolta dei dati di simulazione: {0}. Assicurarsi di avere eseguito l''accesso con il proprio account utente.
       usernameAndPasswordMustBothBeSet=Nome utente e password devono essere entrambi alimentati o vuoti
       explainUserName=Nome utente da utilizzare per l''autenticazione nell''anagrafica
       explainPassword=Password da utilizzare per l''autenticazione nell''anagrafica
      +tracTracConnection=Connessione TracTrac
       addTracTracConnection=Aggiungi connessione TracTrac
      -editTracTracConnection=Modifica connessione TracTrac
       filterTracTracConnections=Filtra connessioni TracTrac
       creatorName=Nome autore
      +swissTimingAchivedConnection=Connessione SwissTimingArchived
       addSwissTimingAchivedConnection=Aggiungi connessione SwissTimingArchived
      -editSwissTimingAchivedConnection=Modifica connessione SwissTimingArchived
       filterSwissTimingAchivedConnections=Filtra connessioni SwissTimingArchived
      -editSwissTimingConnections=Modifica connessione SwissTiming
      +swissTimingConnections=Connessione SwissTiming
       filterSwissTimingConnections=Filtra connessioni SwissTiming
       addSwissTimingConnection=Aggiungi connessione SwissTiming
       updateURL=Aggiorna URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=Distanza sopravento
       errorWhileLoadingUserSettings=Errore durante il caricamento delle impostazioni utente: {0}
       certificates=Certificati
       dataBy=Dati per
      +currentPositionLatitudeTooltip=Latitudine della posizione in gradi (valori positivi indicano l''emisfero settentrionale, valori negativi indicano l''emisfero meridionale)
      +currentPositionLongitudeTooltip=Longitudine della posizione in gradi (valori positivi indicano l''est, valori negativi indicano l''ovest)
      +courseCreation=Creazione della rotta
      +filterMarkTemplateByName=Filtra modelli di segnale
      +markTemplates=Modelli di segnale
      +filterMarkPropertiesByName=Filtra proprietà del segnale
      +markProperties=Proprietà del segnale
      +filterCourseTemplateByName=Filtra modelli di rotta
      +courseTemplates=Modelli di rotta
      +markTemplate=Modello di segnale
      +markRole1=Ruolo segnale 1
      +markRole2=Ruolo segnale 2
      +pleaseSelectAValidMarkTemplate=Selezionare un modello di segnale valido
      +pleaseSelectAValidPassingInstruction=Selezionare un''istruzione per il passaggio valida
      +doYouReallyWantToRemoveCourseTemplate=Eliminare il modello di rotta "{0}"? I collegamenti a tutte le rotte associate verranno annullati.
      +couldNotRemoveCourseTemplate=Impossibile eliminare il modello di rotta "{0}".
      +doYouReallyWantToRemoveMarkProperties=Confermare l''eliminazione delle proprietà del segnale "{0}"?
      +couldNotRemoveMarkProperties=Impossibile eliminare proprietà segnale "{0}".
      +markRoles=Ruoli segnale
      +filterMarkRoles=Filtra ruoli segnale
      +wayPointRequiresPassingInstruction=Selezionare un''istruzione per il passaggio valida per il waypoint
      +wayPointRequiresShortName=Inserire un nome breve per il waypoint
      +wayPointRequiresName=Inserire un nome per il waypoint
      +wayPointMarkTemplatesAreTheSame=I modelli di segnale utilizzati in un waypoint non possono essere gli stessi
      +wayPointMarkInSequenceMissing=Il segnale utilizzato in Sequenza non esiste nel Modello di rotta
      +defaultNumberOfLaps=# predefinito di giri
      +invalidImageURL=URL immagine non valido
      +setDeviceIdentifier=Imposta identificatore dispositivo
      +setPosition=Imposta la posizione
      +spareMarksAndTheirOptionalDefaultMarkRoles=Segnali di scorta e relativi ruoli segnale predefiniti opzionali
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties
      index 70773d02f95..1859280d3f3 100755
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=URL を入力してください。
       pleaseEnterNonEmptyUrlOrUploadImage=URL を入力するか、画像をアップロードしてください。
       addResultImportUrl=結果インポート URL の追加
       errorAddingResultImportUrl=結果インポート URL を追加しようとしてエラーが発生: {0}
      +eventIdOrUrl=イベント ID または URL
      +doYouReallyWantToRemoveResultImportUrls=選択した結果インポート URL を削除しますか
       remoteServerInstances=リモートサーバインスタンス
       localServer=ローカルサーバ
       errorRefreshingSailingServers=SailingServer インスタンスをリフレッシュしようとしてエラーが発生: {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=デスクトップバージョンにジャンプ
       goBack=戻る
       showAllX=全 {0} を表示
       collapseX={0} を圧縮
      +
       numberOfGPSFixes=GPS フィックス
       fastestSailor=最速
       highestSpeed=最高速度
      @@ -2242,18 +2245,18 @@ invalidSecret=無効なシークレット
       warningSailInsightVersion=このサーバは SAP Sail Insight 1 で使用するために設定されています。オープンレガッタは SAP Sail Insight 2 でのみサポートされています。これにより使用に問題が生じる可能性があります
       invalidState=無効なステータス
       doYouReallyWantToRemoveDeviceConfiguration=デバイス設定 {0} を削除しますか
      -errorFetchingSimulationData=シミュレーションデータのフェッチ時にエラーが発生しました: {0}
      +errorFetchingSimulationData=シミュレーションデータのフェッチ時にエラーが発生しました: {0}。自分のユーザアカウントでサインインしているか確認してください。
       usernameAndPasswordMustBothBeSet=ユーザ名とパスワードは両方とも入力するか、両方とも空である必要があります
       explainUserName=マスタ上で認証のために使用するユーザ名
       explainPassword=マスタ上で認証のために使用するパスワード
      +tracTracConnection=TracTrac 接続
       addTracTracConnection=TracTrac 接続の追加
      -editTracTracConnection=TracTrac 接続の編集
       filterTracTracConnections=TracTrac 接続のフィルタリング
       creatorName=作成者名
      +swissTimingAchivedConnection=SwissTimingArchived 接続
       addSwissTimingAchivedConnection=SwissTimingArchived 接続の追加
      -editSwissTimingAchivedConnection=SwissTimingArchived 接続の編集
       filterSwissTimingAchivedConnections=SwissTimingArchived 接続のフィルタリング
      -editSwissTimingConnections=SwissTiming 接続の編集
      +swissTimingConnections=SwissTiming 接続
       filterSwissTimingConnections=SwissTiming 接続のフィルタリング
       addSwissTimingConnection=SwissTiming 接続の追加
       updateURL=URL 更新
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=Windward/Leeward (
       errorWhileLoadingUserSettings=ユーザ設定のロード中にエラーが発生: {0}
       certificates=証明書
       dataBy=データ提供
      +currentPositionLatitudeTooltip=位置の緯度 (度単位: プラスの値は北緯を、マイナスの値は南緯を意味)
      +currentPositionLongitudeTooltip=位置の経度 (度単位: プラスの値は東経、マイナスの値は西経を意味)
      +courseCreation=コース作成
      +filterMarkTemplateByName=マークテンプレートのフィルタリング
      +markTemplates=マークテンプレート
      +filterMarkPropertiesByName=マークプロパティのフィルタリング
      +markProperties=マークプロパティ
      +filterCourseTemplateByName=コーステンプレートのフィルタリング
      +courseTemplates=コーステンプレート
      +markTemplate=マークテンプレート
      +markRole1=マークロール 1
      +markRole2=マークロール 2
      +pleaseSelectAValidMarkTemplate=有効なマークテンプレートを選択してください
      +pleaseSelectAValidPassingInstruction=有効な通過指示を選択してください
      +doYouReallyWantToRemoveCourseTemplate=コーステンプレート "{0}" を削除しますか。関連付けられているコースはすべてリンク解除されます。
      +couldNotRemoveCourseTemplate=コーステンプレート "{0}" を削除できませんでした。
      +doYouReallyWantToRemoveMarkProperties=マークプロパティ "{0}" を削除しますか。
      +couldNotRemoveMarkProperties=マークプロパティ "{0}" を削除できませんでした。
      +markRoles=マークロール
      +filterMarkRoles=マークロールのフィルタリング
      +wayPointRequiresPassingInstruction=変針点の有効な通過指示を選択してください
      +wayPointRequiresShortName=変針点の名前 (短) を入力してください
      +wayPointRequiresName=変針点の名前を入力してください
      +wayPointMarkTemplatesAreTheSame=変針点で使用するマークテンプレートは同じにはできません
      +wayPointMarkInSequenceMissing=順序で使用するマークがコーステンプレートに存在していません
      +defaultNumberOfLaps=デフォルトのラップ数
      +invalidImageURL=画像 URL が無効です
      +setDeviceIdentifier=デバイス ID を設定してください
      +setPosition=位置を設定してください
      +spareMarksAndTheirOptionalDefaultMarkRoles=予備のマークとそのオプションのデフォルトマークロール
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties
      index dd57c1df7e0..85aed198d79 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=Insira um URL.
       pleaseEnterNonEmptyUrlOrUploadImage=Insira um URL ou carregue uma imagem.
       addResultImportUrl=Adicionar URL de importação de resultado
       errorAddingResultImportUrl=Erro ao adicionar URL de importação de resultado: {0}
      +eventIdOrUrl=ID de evento ou URL
      +doYouReallyWantToRemoveResultImportUrls=Remover os URLs de importação de resultados selecionados?
       remoteServerInstances=Instâncias do servidor remoto
       localServer=Servidor local
       errorRefreshingSailingServers=Erro ao atualizar instâncias de SailingServer: {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=Ir para versão desktop
       goBack=Voltar
       showAllX=Visualizar todos os {0}
       collapseX=Comprimir {0}
      +
       numberOfGPSFixes=Pontos fixos de GPS
       fastestSailor=O mais rápido
       highestSpeed=Velocidade mais alta
      @@ -2242,18 +2245,18 @@ invalidSecret=Chave secreta inválida
       warningSailInsightVersion=O servidor está configurado para utilização com o SAP Sail Insight 1. As regatas abertas só são suportadas no SAP Sail Insight 2. Isto pode causar problemas na utilização
       invalidState=Status inválido
       doYouReallyWantToRemoveDeviceConfiguration=Você quer realmente remover a configuração do dispositivo {0}?
      -errorFetchingSimulationData=Ocorreu um erro ao chamar dados de simulação: {0}
      +errorFetchingSimulationData=Ocorreu um erro ao chamar dados de simulação: {0}. Assegure que está conectado com sua conta de usuário.
       usernameAndPasswordMustBothBeSet=Nome de utilizador e senha devem estar ambos preenchidos ou vazios
       explainUserName=Nome de usuário para utilizar para autenticação no mestre
       explainPassword=Senha para utilizar para autenticação no mestre
      +tracTracConnection=Conexão TracTrac
       addTracTracConnection=Adicionar conexão TracTrac
      -editTracTracConnection=Processar conexão TracTrac
       filterTracTracConnections=Filtrar conexões TracTrac
       creatorName=Nome do autor
      +swissTimingAchivedConnection=Conexão SwissTimingArchived
       addSwissTimingAchivedConnection=Adicionar conexão SwissTimingArchived
      -editSwissTimingAchivedConnection=Processar conexão SwissTimingArchived
       filterSwissTimingAchivedConnections=Filtrar conexões SwissTimingArchived
      -editSwissTimingConnections=Processar conexão SwissTiming
      +swissTimingConnections=Conexão SwissTiming
       filterSwissTimingConnections=Filtrar conexões SwissTiming
       addSwissTimingConnection=Adicionar conexão SwissTiming
       updateURL=Atualizar URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=Barlavento/sotavent
       errorWhileLoadingUserSettings=Erro ao carregar configurações do usuário: {0}
       certificates=Certificados
       dataBy=Dados por
      +currentPositionLatitudeTooltip=Latitude de posição em graus (valores positivos designam o hemisfério norte, valores negativos designam o hemisfério sul)
      +currentPositionLongitudeTooltip=Longitude de posição em graus (valores positivos designam este, valores negativos designam oeste)
      +courseCreation=Criação de percurso
      +filterMarkTemplateByName=Filtrar modelos de marca
      +markTemplates=Modelos de marca
      +filterMarkPropertiesByName=Filtrar propriedades de marca
      +markProperties=Propriedades de marca
      +filterCourseTemplateByName=Filtrar modelos de percurso
      +courseTemplates=Modelos de percurso
      +markTemplate=Modelo de marca
      +markRole1=Função de marca 1
      +markRole2=Função de marca 2
      +pleaseSelectAValidMarkTemplate=Selecione um modelo de marca válido
      +pleaseSelectAValidPassingInstruction=Selecione uma instrução de passagem válida
      +doYouReallyWantToRemoveCourseTemplate=Você quer realmente remover modelo de percurso"{0}"? Todos os percursos associados serão desvinculados.
      +couldNotRemoveCourseTemplate=Não foi possível remover modelo de percurso "{0}".
      +doYouReallyWantToRemoveMarkProperties=Você quer realmente remover as propriedades da marca "{0}"?
      +couldNotRemoveMarkProperties=Não foi possível remover as propriedades da marca "{0}".
      +markRoles=Funções de marca
      +filterMarkRoles=Filtrar funções de marca
      +wayPointRequiresPassingInstruction=Selecione uma instrução de passagem válida para o waypoint
      +wayPointRequiresShortName=Insira um nome curto para o waypoint
      +wayPointRequiresName=Insira o nome para o waypoint
      +wayPointMarkTemplatesAreTheSame=Os modelos de marca utilizados em um waypoint não podem ser iguais
      +wayPointMarkInSequenceMissing=A marca utilizada na sequência não existe no modelo do percurso
      +defaultNumberOfLaps=Nº padrão de voltas
      +invalidImageURL=O URL da imagem é inválido
      +setDeviceIdentifier=Definir identificador de dispositivo
      +setPosition=Definir posição
      +spareMarksAndTheirOptionalDefaultMarkRoles=Marcas sobressalentes e suas funções de marca padrão opcionais
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
      index 06b78485ec9..749668c3402 100755
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=Введите URL-адрес.
       pleaseEnterNonEmptyUrlOrUploadImage=Введите URL-адрес или загрузите изображение.
       addResultImportUrl=Добавьте URL-адрес импорта результатов
       errorAddingResultImportUrl=Ошибка при добавлении URL-адресов импорта результатов: {0}
      +eventIdOrUrl=Ид. события или URL
      +doYouReallyWantToRemoveResultImportUrls=Действительно удалить выбранные URL-адреса импорта результатов?
       remoteServerInstances=Удаленные экземпляры сервера
       localServer=Локальный сервер
       errorRefreshingSailingServers=Ошибка при обновлении экземпляров SailingServer: {0}
      @@ -666,7 +668,7 @@ timePointMustBeAfterStartOfTracking=Отметка времени должна 
       timePointMustBeBeforeEndOfTracking=Отметка времени должна быть раньше окончания отслеживания.
       regattaUsedForTheTrackedRace=Регата, используемая для отслеживаемой гонки
       tracTracUpdateUrl=URI обновления сервера TracTrac
      -passingInstructions=Передача инструкций
      +passingInstructions=Инструкции по прохождению
       refreshNow=Обновить сейчас
       stopUpdating=Прекратить обновление
       startUpdating=Начать обновление
      @@ -1436,6 +1438,7 @@ gotoDesktop=Перейти на версию для рабочего стола
       goBack=Назад
       showAllX=Показать все {0}
       collapseX=Свернуть {0}
      +
       numberOfGPSFixes=Замеры GPS
       fastestSailor=Самый быстрый
       highestSpeed=Наивысшая скорость
      @@ -2242,18 +2245,18 @@ invalidSecret=Недействительный секрет
       warningSailInsightVersion=Сервер сконфигурирован для использования с SAP Sail Insight 1. Открытые регаты поддерживаются только в SAP Sail Insight 2. Это может стать причиной проблем при использовании
       invalidState=Недействительный статус
       doYouReallyWantToRemoveDeviceConfiguration=Действительно удалить конфигурацию устройства {0}?
      -errorFetchingSimulationData=Произошла ошибка при вызове данных моделирования: {0}
      +errorFetchingSimulationData=Произошла ошибка при вызове данных моделирования: {0}. Убедитесь, что вход выполнен с вашей учетной записью.
       usernameAndPasswordMustBothBeSet=Имя пользователя и пароль должны быть либо заполненными, либо пустыми
       explainUserName=Имя пользователя для аутентификации на главном сервере
       explainPassword=Пароль для аутентификации на главном сервере
      +tracTracConnection=Соединение TracTrac
       addTracTracConnection=Добавить соединение TracTrac
      -editTracTracConnection=Редактировать соединение TracTrac
       filterTracTracConnections=Фильтр соединений TracTrac
       creatorName=Имя автора
      +swissTimingAchivedConnection=Соединение SwissTimingArchived
       addSwissTimingAchivedConnection=Добавить соединение SwissTimingArchived
      -editSwissTimingAchivedConnection=Редактировать соединение SwissTimingArchived
       filterSwissTimingAchivedConnections=Фильтр соединений SwissTimingArchived
      -editSwissTimingConnections=Редактировать соединение SwissTiming
      +swissTimingConnections=Соединение SwissTiming
       filterSwissTimingConnections=Фильтр соединений SwissTiming
       addSwissTimingConnection=Добавить соединение SwissTiming
       updateURL=Обновить URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=На ветре/п
       errorWhileLoadingUserSettings=Ошибка при загрузке пользовательских настроек: {0}
       certificates=Сертификаты
       dataBy=Данные по
      +currentPositionLatitudeTooltip=Широта позиции в градусах (положительные значения — северное полушарие, отрицательные значения — южное полушарие)
      +currentPositionLongitudeTooltip=Долгота позиции в градусах (положительные значения — восток, отрицательные значения — запад)
      +courseCreation=Создание курса
      +filterMarkTemplateByName=Фильтровать шаблоны знаков
      +markTemplates=Шаблоны знаков
      +filterMarkPropertiesByName=Фильтровать свойства знаков
      +markProperties=Свойства знаков
      +filterCourseTemplateByName=Фильтровать шаблоны курсов
      +courseTemplates=Шаблоны курсов
      +markTemplate=Шаблон знака
      +markRole1=Роль знака 1
      +markRole2=Роль знака 2
      +pleaseSelectAValidMarkTemplate=Выберите допустимый шаблон знака
      +pleaseSelectAValidPassingInstruction=Выберите допустимую инструкцию по прохождению
      +doYouReallyWantToRemoveCourseTemplate=Действительно удалить шаблон курса "{0}"? Все связанные курсы будут отвязаны.
      +couldNotRemoveCourseTemplate=Не удалось удалить шаблон курс "{0}".
      +doYouReallyWantToRemoveMarkProperties=Действительно удалить свойства знака "{0}"?
      +couldNotRemoveMarkProperties=Не удалось удалить свойства знака "{0}".
      +markRoles=Роли знаков
      +filterMarkRoles=Фильтровать роли знаков
      +wayPointRequiresPassingInstruction=Выберите допустимую инструкцию по прохождению для путевой точки
      +wayPointRequiresShortName=Введите краткое имя для путевой точки
      +wayPointRequiresName=Введите имя для путевой точки
      +wayPointMarkTemplatesAreTheSame=Шаблоны знаков, используемые в путевой точке, не могут быть одинаковыми
      +wayPointMarkInSequenceMissing=Знак, используемый в последовательности, не существует в шаблоне курса
      +defaultNumberOfLaps=Число кругов по умолчанию
      +invalidImageURL=URL изображения недействителен
      +setDeviceIdentifier=Установить идентификатор устройства
      +setPosition=Установить позицию
      +spareMarksAndTheirOptionalDefaultMarkRoles=Запасные знаки и их роли по умолчанию (необязательно)
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties
      index 0a5d449c815..e969df5089f 100755
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties
      @@ -438,6 +438,8 @@ pleaseEnterNonEmptyUrl=请输入 URL。
       pleaseEnterNonEmptyUrlOrUploadImage=请输入 URL 或上传图像。
       addResultImportUrl=添加结果导入 URL
       errorAddingResultImportUrl=添加结果导入 URL 出错: {0}
      +eventIdOrUrl=活动编号或 URL
      +doYouReallyWantToRemoveResultImportUrls=是否确定移除所选结果导入 URL?
       remoteServerInstances=远程服务器实例
       localServer=本地服务器
       errorRefreshingSailingServers=刷新 SailingServer 实例出错: {0}
      @@ -1436,6 +1438,7 @@ gotoDesktop=进入桌面版
       goBack=返回
       showAllX=显示所有{0}
       collapseX=折叠{0}
      +
       numberOfGPSFixes=GPS 修复
       fastestSailor=最快
       highestSpeed=最高速度
      @@ -2242,18 +2245,18 @@ invalidSecret=密钥无效
       warningSailInsightVersion=此服务器配置为与 SAP Sail Insight 一起使用 1. 仅 SAP Sail Insight 中支持公开赛 2. 这可能会导致使用中出现问题
       invalidState=状态无效
       doYouReallyWantToRemoveDeviceConfiguration=是否确定移除设备配置{0}?
      -errorFetchingSimulationData=获取模拟数据时发生错误:{0}
      +errorFetchingSimulationData=获取模拟数据时发生错误:{0}。请确保您已经登录您的用户账户。
       usernameAndPasswordMustBothBeSet=用户名和密码必须填写或为空
       explainUserName=用于在主服务器上进行身份验证的用户名
       explainPassword=用于在主服务器上进行身份验证的密码
      +tracTracConnection=TracTrac 连接
       addTracTracConnection=添加 TracTrac 连接
      -editTracTracConnection=编辑 TracTrac 连接
       filterTracTracConnections=筛选 TracTrac 连接
       creatorName=创建者名称
      +swissTimingAchivedConnection=SwissTimingArchived 连接
       addSwissTimingAchivedConnection=添加 SwissTimingArchived 连接
      -editSwissTimingAchivedConnection=编辑 SwissTimingArchived 连接
       filterSwissTimingAchivedConnections=筛选 SwissTimingArchived 连接
      -editSwissTimingConnections=编辑 SwissTiming 连接
      +swissTimingConnections=SwissTiming 连接
       filterSwissTimingConnections=筛选 SwissTiming 连接
       addSwissTimingConnection=添加 SwissTiming 连接
       updateURL=更新 URL
      @@ -2323,3 +2326,33 @@ orcPerformanceCurveLegTypeDescriptionWindwardLeewardRealLive=上风/下风(向
       errorWhileLoadingUserSettings=加载用户设置时发生错误:{0}
       certificates=证书
       dataBy=数据排序方式
      +currentPositionLatitudeTooltip=位置的纬度(正值表示北半球,负值表示南半球)
      +currentPositionLongitudeTooltip=位置的经度(正值表示东,负值表示西)
      +courseCreation=场地创建
      +filterMarkTemplateByName=筛选标志模板
      +markTemplates=标志模板
      +filterMarkPropertiesByName=筛选标志属性
      +markProperties=标志属性
      +filterCourseTemplateByName=筛选场地模板
      +courseTemplates=场地模板
      +markTemplate=标志模板
      +markRole1=标志角色 1
      +markRole2=标志角色 2
      +pleaseSelectAValidMarkTemplate=请选择有效的标志模板
      +pleaseSelectAValidPassingInstruction=请选择有效的通过指示
      +doYouReallyWantToRemoveCourseTemplate=是否确定移除场地模板“{0}”?所有关联的场地都将被取消链接。
      +couldNotRemoveCourseTemplate=无法移除场地模板“{0}”。
      +doYouReallyWantToRemoveMarkProperties=是否确定移除标志属性“{0}”?
      +couldNotRemoveMarkProperties=无法移除标志属性“{0}”。
      +markRoles=标志角色
      +filterMarkRoles=筛选标志角色
      +wayPointRequiresPassingInstruction=请选择有效的航路点通过指示
      +wayPointRequiresShortName=请输入航路点的短名称
      +wayPointRequiresName=请输入航路点的名称
      +wayPointMarkTemplatesAreTheSame=航路点中使用的标志模板不得相同
      +wayPointMarkInSequenceMissing=序列中使用的标志在场地模板中不存在
      +defaultNumberOfLaps=间隔的默认编号
      +invalidImageURL=图像 URL 无效
      +setDeviceIdentifier=设置设备标识符
      +setPosition=设置位置
      +spareMarksAndTheirOptionalDefaultMarkRoles=备用标志及其可选默认标志角色
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_es.properties
      index 10c3f98e92b..e5c5920123c 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_es.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_es.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=No existe el registro de carrera
       tagNotRevokable=Esta etiqueta no se puede revocar
       tagAlreadyExists=Ya existe la etiqueta, no se permiten etiquetas duplicadas
       invalidParameters=Uno de los parámetros indicados no tiene un valor válido
      +pleaseEnterNonEmptyUrl=Introduzca un URL no vacío
      +scoreCorrectionProviderNotFound=No existe proveedor de corrección de puntuación
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_fr.properties
      index d91349e3cae..8a36b662b9f 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_fr.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_fr.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=Journal de la course introuvable
       tagNotRevokable=Cette balise ne peut pas être annulée.
       tagAlreadyExists=La balise existe déjà. Les balises en double ne sont pas autorisées.
       invalidParameters=L''un des paramètres donnés a une valeur non valide.
      +pleaseEnterNonEmptyUrl=Veuillez saisir une URL non vide.
      +scoreCorrectionProviderNotFound=Fournisseur de correction des scores introuvable
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_it.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_it.properties
      index f545852684e..31fc2cf3cea 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_it.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_it.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=Registro di gara non trovato.
       tagNotRevokable=Impossibile revocare questo tag.
       tagAlreadyExists=Tag già esistente; tag doppi non ammessi.
       invalidParameters=Uno dei parametri indicati non ha un valore valido.
      +pleaseEnterNonEmptyUrl=Inserire un URL non vuoto
      +scoreCorrectionProviderNotFound=Provider di correzione dei punteggi non trovato
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ja.properties
      index 901051eacab..04bb001bffe 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ja.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ja.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=レースログが見つかりません
       tagNotRevokable=このタグは取消ができません
       tagAlreadyExists=タグがすでに存在しています。タグの重複は許されていません
       invalidParameters=指定されたパラメータの 1 つに有効な値がありません
      +pleaseEnterNonEmptyUrl=空でない URL を入力してください
      +scoreCorrectionProviderNotFound=得点修正の提供者が見つかりません
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_pt.properties
      index dd44111beb0..0b39beddbac 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_pt.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_pt.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=RaceLog não encontrado.
       tagNotRevokable=Esta etiqueta não pode ser anulada.
       tagAlreadyExists=A etiqueta já existe, não são permitidas etiquetas duplicadas.
       invalidParameters=Um dos parâmetros indicados não tem um valor válido.
      +pleaseEnterNonEmptyUrl=Insira um URL que não esteja vazio
      +scoreCorrectionProviderNotFound=Fornecedor de correção de pontuação não encontrado
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ru.properties
      index 35b6eb9fb1f..43095d2b7cf 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ru.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_ru.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=Журнал гонки не найден!
       tagNotRevokable=Этот тег невозможно отозвать!
       tagAlreadyExists=Тег уже существует, дубликаты тегов не разрешены!
       invalidParameters=Один из указанных параметров не имеет действительного значения!
      +pleaseEnterNonEmptyUrl=Введите непустой URL-адрес
      +scoreCorrectionProviderNotFound=Поставщик корректировки оценки не найден
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_zh.properties
      index dcfdffa698f..6c9abe43dd7 100644
      --- a/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_zh.properties
      +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/stringmessages/StringMessages_zh.properties
      @@ -10,3 +10,5 @@ raceLogNotFound=未找到比赛轮次日志!
       tagNotRevokable=无法撤销此标记!
       tagAlreadyExists=标记已存在,不允许重复的标记!
       invalidParameters=其中一个给定参数没有有效值!
      +pleaseEnterNonEmptyUrl=请输入非空 URL
      +scoreCorrectionProviderNotFound=未找到得分更正提供方
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_es.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_es.properties
      index 51231d760e4..638c4c381fc 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_es.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_es.properties
      @@ -190,3 +190,4 @@ permission=Autorización
       nullUserGroup=
       editRolesAndPermissionsForUser=Editar roles y autorizaciones para el usuario {0}
       permissionType=Tipo de autorización
      +couldNotLoadMarkTemplates=No se han podido cargar las plantillas de marcas.
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_fr.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_fr.properties
      index 3142b231d5a..967833cd319 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_fr.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_fr.properties
      @@ -190,3 +190,4 @@ permission=Autorisation
       nullUserGroup=
       editRolesAndPermissionsForUser=Modifier les rôles et autorisations pour l''utilisateur {0}
       permissionType=Type d''autorisation
      +couldNotLoadMarkTemplates=Impossible de charger les modèles de marques.
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_it.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_it.properties
      index 291699274bd..8b8cb1ed92d 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_it.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_it.properties
      @@ -190,3 +190,4 @@ permission=Autorizzazione
       nullUserGroup=
       editRolesAndPermissionsForUser=Elabora ruoli e autorizzazioni per l''utente {0}
       permissionType=Tipo di autorizzazione
      +couldNotLoadMarkTemplates=Impossibile caricare modelli segnale.
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ja.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ja.properties
      index fa68f6ceac9..72a43d9e693 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ja.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ja.properties
      @@ -190,3 +190,4 @@ permission=権限
       nullUserGroup=
       editRolesAndPermissionsForUser=ユーザ {0} のロールと権限を編集
       permissionType=権限タイプ
      +couldNotLoadMarkTemplates=マークテンプレートをロードできませんでした。
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_pt.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_pt.properties
      index 760b0924b52..0229763faf1 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_pt.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_pt.properties
      @@ -190,3 +190,4 @@ permission=Autorização
       nullUserGroup=
       editRolesAndPermissionsForUser=Processar funções e autorizações para usuário {0}
       permissionType=Tipo de autorização
      +couldNotLoadMarkTemplates=Não foi possível carregar modelos de marca.
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ru.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ru.properties
      index 07b7cff1a2a..2bdeecc16f4 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ru.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_ru.properties
      @@ -190,3 +190,4 @@ permission=Полномочия
       nullUserGroup=
       editRolesAndPermissionsForUser=Редактировать роли и полномочия для пользователя {0}
       permissionType=Тип полномочий
      +couldNotLoadMarkTemplates=Не удалось загрузить шаблоны знаков.
      diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh.properties
      index 630a476ec46..9e852c9c6ca 100644
      --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh.properties
      +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_zh.properties
      @@ -190,3 +190,4 @@ permission=权限
       nullUserGroup=
       editRolesAndPermissionsForUser=为用户{0}编辑角色和权限
       permissionType=权限类型
      +couldNotLoadMarkTemplates=无法加载标记模板。
      
      From 4a6c5dd26999bfdb2fdd0b7f9f354a72c36eaba1 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Thu, 6 Feb 2020 19:55:41 +0100
      Subject: [PATCH 44/85] added missing file for bug5160
      
      ---
       .../CourseBaseWithGeometryJsonSerializer.java | 76 +++++++++++++++++++
       1 file changed, 76 insertions(+)
       create mode 100755 java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java
      
      diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java
      new file mode 100755
      index 00000000000..7248b503f6f
      --- /dev/null
      +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/coursedata/impl/CourseBaseWithGeometryJsonSerializer.java
      @@ -0,0 +1,76 @@
      +package com.sap.sailing.server.gateway.serialization.coursedata.impl;
      +
      +import java.util.Map;
      +
      +import org.json.simple.JSONObject;
      +
      +import com.sap.sailing.domain.base.CourseBase;
      +import com.sap.sailing.domain.base.Leg;
      +import com.sap.sailing.domain.base.Waypoint;
      +import com.sap.sailing.server.gateway.serialization.JsonSerializer;
      +import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseWithGeometryJsonSerializer.CourseGeometry;
      +import com.sap.sse.common.Bearing;
      +import com.sap.sse.common.Distance;
      +import com.sap.sse.common.Util.Pair;
      +
      +public class CourseBaseWithGeometryJsonSerializer implements JsonSerializer> {
      +    public static final String FIELD_TOTAL_DISTANCE_IN_METERS = "totalDistanceInMeters";
      +    public static final String FIELD_LEG_DISTANCE_IN_METERS = "legDistanceInMeters";
      +    public static final String FIELD_LEG_BEARING_TRUE_DEGREES = "legBearingTrueDegrees";
      +
      +    public static class CourseGeometry {
      +        private final Distance totalDistance;
      +        private final Map legDistances;
      +        private final Map legBearings;
      +        
      +        public CourseGeometry(Distance totalDistance, Map legDistances, Map legBearings) {
      +            super();
      +            this.totalDistance = totalDistance;
      +            this.legDistances = legDistances;
      +            this.legBearings = legBearings;
      +        }
      +
      +        public Distance getTotalDistance() {
      +            return totalDistance;
      +        }
      +
      +        public Map getLegDistances() {
      +            return legDistances;
      +        }
      +
      +        public Map getLegBearings() {
      +            return legBearings;
      +        }
      +    }
      +
      +    private JsonSerializer waypointSerializer;
      +
      +    public CourseBaseWithGeometryJsonSerializer(WaypointJsonSerializer waypointSerializer) {
      +        this.waypointSerializer = waypointSerializer;
      +    }
      +
      +    @Override
      +    public JSONObject serialize(final Pair courseAndOptionalGeometry) {
      +        final CourseBase course = courseAndOptionalGeometry.getA();
      +        final CourseGeometry geometry = courseAndOptionalGeometry.getB();
      +        final JSONObject result = new CourseBaseJsonSerializer(waypointSerializer) {
      +            @Override
      +            protected JSONObject serializeWaypoint(Waypoint waypoint) {
      +                final JSONObject result = super.serializeWaypoint(waypoint);
      +                if (geometry != null) {
      +                    final int waypointIndex = course.getIndexOfWaypoint(waypoint);
      +                    if (waypointIndex >= 0 && course.getLegs().size() > waypointIndex) {
      +                        final Leg leg = course.getLegs().get(waypointIndex);
      +                        result.put(FIELD_LEG_DISTANCE_IN_METERS, geometry.getLegDistances().get(leg).getMeters());
      +                        result.put(FIELD_LEG_BEARING_TRUE_DEGREES, geometry.getLegBearings().get(leg).getDegrees());
      +                    }
      +                }
      +                return result;
      +            }
      +        }.serialize(course);
      +        if (courseAndOptionalGeometry.getB() != null) {
      +            result.put(FIELD_TOTAL_DISTANCE_IN_METERS, courseAndOptionalGeometry.getB().getTotalDistance().getMeters());
      +        }
      +        return result;
      +    }
      +}
      
      From d0790007f8a553f4b5f9d9cb945a26f3b91bd3e1 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Thu, 6 Feb 2020 22:08:57 +0100
      Subject: [PATCH 45/85] bug5159: added startanalysis method to RegattasResource
      
      ---
       .../gateway/jaxrs/api/RegattasResource.java   | 87 +++++++++++++++++++
       1 file changed, 87 insertions(+)
      
      diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      index f6387974a15..3275c84dafd 100644
      --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
      @@ -4,6 +4,7 @@ import java.io.Serializable;
       import java.net.URI;
       import java.net.URISyntaxException;
       import java.util.ArrayList;
      +import java.util.Arrays;
       import java.util.Collections;
       import java.util.Date;
       import java.util.HashMap;
      @@ -11,9 +12,11 @@ import java.util.HashSet;
       import java.util.Iterator;
       import java.util.List;
       import java.util.Map;
      +import java.util.Map.Entry;
       import java.util.Set;
       import java.util.UUID;
       import java.util.concurrent.Callable;
      +import java.util.concurrent.ExecutionException;
       import java.util.function.Function;
       import java.util.logging.Level;
       import java.util.logging.Logger;
      @@ -77,7 +80,11 @@ import com.sap.sailing.domain.common.Tack;
       import com.sap.sailing.domain.common.TargetTimeInfo;
       import com.sap.sailing.domain.common.WindSource;
       import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
      +import com.sap.sailing.domain.common.dto.CompetitorDTO;
       import com.sap.sailing.domain.common.dto.FleetDTO;
      +import com.sap.sailing.domain.common.dto.LeaderboardDTO;
      +import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO;
      +import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
       import com.sap.sailing.domain.common.security.SecuredDomainType;
       import com.sap.sailing.domain.common.tracking.GPSFix;
       import com.sap.sailing.domain.common.tracking.GPSFixMoving;
      @@ -1267,6 +1274,86 @@ public class RegattasResource extends AbstractSailingServerResource {
               }
               return response;
           }
      +    
      +    /**
      +     * Calculates all start analysis parameters for all competitors in the race.
      +     */
      +    @GET
      +    @Produces("application/json;charset=UTF-8")
      +    @Path("{regattaname}/races/{racename}/startanalysis")
      +    public Response getStartAnalysis(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
      +            @QueryParam("secret") String regattaSecret) {
      +        Response response = null;
      +        Regatta regatta = findRegattaByName(regattaName);
      +        if (regatta == null) {
      +            response = getBadRegattaErrorResponse(regattaName);
      +        } else {
      +            if (!getService().skipChecksDueToCorrectSecret(regattaName, regattaSecret)) {
      +                getSecurityService().checkCurrentUserReadPermission(regatta);
      +            }
      +            final TrackedRace trackedRace = findTrackedRace(regatta, raceName);
      +            if (trackedRace == null) {
      +                response = getBadRaceErrorResponse(regattaName, raceName);
      +            } else {
      +                try {
      +                    JSONArray jsonStartAnalysis = getStartAnalysis(trackedRace, regatta);
      +                    String json = jsonStartAnalysis.toJSONString();
      +                    response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
      +                } catch (NoWindException | InterruptedException | ExecutionException e) {
      +                    response = Response.status(Status.INTERNAL_SERVER_ERROR)
      +                            .entity("Error computing start analysis for race '" + StringEscapeUtils.escapeHtml(raceName) + "' in regatta '" + StringEscapeUtils.escapeHtml(regattaName) + "': "+
      +                                    StringEscapeUtils.escapeHtml(e.getMessage()))
      +                            .type(MediaType.TEXT_PLAIN).build();
      +
      +                }
      +            }
      +        }
      +        return response;
      +    }
      +
      +    private JSONArray getStartAnalysis(TrackedRace trackedRace, Regatta regatta) throws NoWindException, InterruptedException, ExecutionException {
      +        assert trackedRace != null;
      +        final Leaderboard leaderboard = getService().getLeaderboardByName(regatta.getName());
      +        final CompetitorJsonSerializer competitorSerializer = new CompetitorJsonSerializer(new TeamJsonSerializer(new PersonJsonSerializer(
      +                new NationalityJsonSerializer())),
      +                BoatJsonSerializer.create(), /* serialize non-public fields */ false);
      +        final JSONArray result = new JSONArray();
      +        if (leaderboard != null) {
      +            final RaceDefinition race = trackedRace.getRace();
      +            final Map competitorByIdAsString = new HashMap<>();
      +            for (final Competitor c : race.getCompetitors()) {
      +                competitorByIdAsString.put(c.getId().toString(), c);
      +            }
      +            final Pair raceColumnAndFleet = regatta.getRaceColumnAndFleet(trackedRace);
      +            if (raceColumnAndFleet != null && raceColumnAndFleet.getA() != null) {
      +                final LeaderboardDTO latestLeaderboard = leaderboard.getLeaderboardDTO(/* live time point */ null,
      +                        Arrays.asList(raceColumnAndFleet.getA().getName()), /* addOverallDetails */ false, getService(),
      +                        getService().getBaseDomainFactory(), /* fillTotalPointsUncorrected */ false);
      +                if (latestLeaderboard != null) {
      +                    for (final Entry e : latestLeaderboard.rows.entrySet()) {
      +                        final Competitor competitor = competitorByIdAsString.get(e.getKey().getIdAsString());
      +                        if (competitor != null) {
      +                            final LeaderboardRowDTO competitorRow = e.getValue();
      +                            final LeaderboardEntryDTO entry = competitorRow.fieldsByRaceColumnName.get(raceColumnAndFleet.getA().getName());
      +                            if (entry != null) {
      +                                final JSONObject competitorStartAnalysisJson = new JSONObject();
      +                                competitorStartAnalysisJson.put("competitor",  competitorSerializer.serialize(competitor));
      +                                competitorStartAnalysisJson.put("distanceToStarboardSideOfStartLineInMeters", entry.distanceToStarboardSideOfStartLineInMeters);
      +                                competitorStartAnalysisJson.put("distanceToStartLineAtStartOfRaceInMeters", entry.distanceToStartLineAtStartOfRaceInMeters);
      +                                competitorStartAnalysisJson.put("distanceToStartLineFiveSecondsBeforeStartInMeters", entry.distanceToStartLineFiveSecondsBeforeStartInMeters);
      +                                competitorStartAnalysisJson.put("speedOverGroundAtPassingStartWaypointInKnots", entry.speedOverGroundAtPassingStartWaypointInKnots);
      +                                competitorStartAnalysisJson.put("speedOverGroundAtStartOfRaceInKnots", entry.speedOverGroundAtStartOfRaceInKnots);
      +                                competitorStartAnalysisJson.put("speedOverGroundFiveSecondsBeforeStartInKnots", entry.speedOverGroundFiveSecondsBeforeStartInKnots);
      +                                competitorStartAnalysisJson.put("timeBetweenRaceStartAndCompetitorStartInSeconds", entry.timeBetweenRaceStartAndCompetitorStartInSeconds);
      +                                result.add(competitorStartAnalysisJson);
      +                            }
      +                        }
      +                    }
      +                }
      +            }
      +        }
      +        return result;
      +    }
       
           /**
            * Gets the relevant times of the race.
      
      From fd648c18adc0dd54c179a8d68d577a645e7d0749 Mon Sep 17 00:00:00 2001
      From: Usman Ali 
      Date: Fri, 7 Feb 2020 15:19:51 +0500
      Subject: [PATCH 46/85] new certificate based changes completed
      
      ---
       .../impl/ORCCertificatesCollectionJSON.java   |  69 +-
       .../resources/rms/newFormatCertificate.json   | 807 +++++-------------
       2 files changed, 246 insertions(+), 630 deletions(-)
      
      diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java
      index 1f6abc16492..3eb4312bec2 100755
      --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java
      +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java
      @@ -60,6 +60,7 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
           private static final String CIRCULAR_RANDOM = "CR";
           private static final String NON_SPINNAKER = "NS";
           private static final String EMPTY_CERT_NO = "      ";
      +    private static final String WIND_SPEEDS = "WindSpeeds";
       
           private Map certificateJsonObjectsByCertificateId;
       
      @@ -134,22 +135,31 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
                   case ALLOWANCES:
                       final JSONObject allowances = (JSONObject) object.get(ALLOWANCES);
                       // calculating the twa and tws before calculating the other dependent values in allowances object
      +                Object windSpeedObject = allowances.get(WIND_SPEEDS);
      +                if (windSpeedObject != null) {
      +                    final JSONArray array = (JSONArray) windSpeedObject;
      +                    for (int count = 0; count < array.size(); count++) {
      +                        Double twsValue = ((Number) array.get(count)).doubleValue();
      +                        trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue));
      +                    }
      +                } else {
      +                    for (Speed speed : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) {
      +                        trueWindSpeedMap.put(speed.getKnots(), speed);
      +                    }
      +                }
      +                final Double [] trueWindSpeedArray = new Double[trueWindSpeedMap.size()];
      +                trueWindSpeedMap.keySet().toArray(trueWindSpeedArray);
                       for (final Object aKey : allowances.keySet()) {
                           String keyString = (String) aKey;
                           boolean found = p.matcher(keyString).lookingAt();
                           if (found) {
                               final JSONArray array = (JSONArray) allowances.get(keyString);
      -                        if (array.size() < 1) {
      -                            break;
      -                        }
      -
                               Map twsMap = new HashMap<>();
                               for (int i = 0; i < array.size(); i++) {
      -                            JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i, null);
      -                            Double tws = (Double) twsAndAllowances.get("tws");
      -                            Double allowance = (Double) twsAndAllowances.get("allowance");
      -                            trueWindSpeedMap.put(tws, new KnotSpeedImpl(tws));
      -                            twsMap.put(trueWindSpeedMap.get(tws), new SecondsDurationImpl(allowance));
      +                            Double allowanceValue = ((Number) array.get(i)).doubleValue();
      +                            twsMap.put(trueWindSpeedMap.get(trueWindSpeedArray[i]),
      +                                    new SecondsDurationImpl(allowanceValue));
      +
                               }
                               Double trueWindAngleValue = Double.parseDouble((keyString).substring(1));
                               trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue));
      @@ -158,27 +168,20 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
                           }
                       }
                       
      -                final Double [] trueWindSpeedArray = new Double[trueWindSpeedMap.size()];
      -                trueWindSpeedMap.keySet().toArray(trueWindSpeedArray);
      +               
                       for (final Object aKey : allowances.keySet()) {
                           final JSONArray array = (JSONArray) allowances.get(aKey);
                           if (((String) aKey).equals(BEAT_ANGLE)) {
                               for (int i = 0; i < array.size(); i++) {
      -                            JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i,
      -                                    trueWindSpeedArray);
      -                            Double tws = (Double) twsAndAllowances.get("tws");
      -                            Double allowance = (Double) twsAndAllowances.get("allowance");
      -                            beatAngles.put(new KnotSpeedImpl(tws), new DegreeBearingImpl(allowance));
      +                            beatAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]),
      +                                    new DegreeBearingImpl(((Number) array.get(i)).doubleValue()));
                               }
                               continue;
                           }
                           if (((String) aKey).equals(GYBE_ANGLE)) {
                               for (int i = 0; i < array.size(); i++) {
      -                            JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i,
      -                                    trueWindSpeedArray);
      -                            Double tws = (Double) twsAndAllowances.get("tws");
      -                            Double allowance = (Double) twsAndAllowances.get("allowance");
      -                            gybeAngles.put(new KnotSpeedImpl(tws), new DegreeBearingImpl(allowance));
      +                            gybeAngles.put(new KnotSpeedImpl(trueWindSpeedArray[i]),
      +                                    new DegreeBearingImpl(((Number) array.get(i)).doubleValue()));
                               }
                               continue;
                           }
      @@ -186,11 +189,8 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
                          //ignore true wind angles key
                           if (!p.matcher((String) aKey).lookingAt()) {
                               for (int i = 0; i < array.size(); i++) {
      -                            JSONObject twsAndAllowances = getTrueWindSpeedAndAllowances(array.get(i), i,
      -                                    trueWindSpeedArray);
      -                            Double tws = (Double) twsAndAllowances.get("tws");
      -                            Double allowance = (Double) twsAndAllowances.get("allowance");
      -                            twsMap.put(new KnotSpeedImpl(tws), new SecondsDurationImpl(allowance));
      +                            twsMap.put(new KnotSpeedImpl(trueWindSpeedArray[i]),
      +                                    new SecondsDurationImpl(((Number) array.get(i)).doubleValue()));
                               }
                           }
                           switch ((String) aKey) {
      @@ -258,23 +258,6 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec
               
           }
       
      -    
      -    private JSONObject getTrueWindSpeedAndAllowances(Object object, int index, Double[] trueWindSpeed) {
      -
      -        JSONObject response = new JSONObject();
      -        if ((object instanceof JSONObject)) {
      -            JSONObject twa = (JSONObject) object;
      -            Double twsValue = (Double) twa.get("twsInKts");
      -            Double allowance = ((Number) twa.get("allInSPNM")).doubleValue();
      -            response.put("tws", twsValue);
      -            response.put("allowance", allowance);
      -        } else {
      -            response.put("tws", trueWindSpeed == null ? ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[index].getKnots()
      -                    : trueWindSpeed[index]);
      -            response.put("allowance", ((Number) object).doubleValue());
      -        }
      -        return response;
      -    }
       
           private String getId(JSONObject certificateAsJson) {
               return getIdFromFields(certificateAsJson.get(NAT_AUTH).toString(),
      diff --git a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      index 9195fc6019e..87030c91d15 100644
      --- a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      +++ b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      @@ -1,588 +1,221 @@
      -{ 
      -   "rms":[ 
      -      
      -	  { 
      -         "NatAuth":"GRE",
      -         "BIN":"GR4988",
      -         "CertNo":"000905",
      -         "RefNo":"GRE01015120",
      -         "SailNo":"GRE-4988",
      -         "YachtName":"GLAFKI II2",
      -         "Class":"ONE-OFF",
      -         "Builder":"MIKE PARRY",
      -         "Designer":"ROB HUMPHREYS",
      -         "Address3":"IO CHANION",
      -         "C_Type":"CLUB",
      -         "Division":"R",
      -         "IssueDate":"2019-03-09T22:09:46.000Z",
      -         "Age_Year":1981,
      -         "CrewWT":520,
      -         "LOA":9.225,
      -         "IMSL":7.118,
      -         "Draft":1.784,
      -         "MB":3.158,
      -         "Dspl_Measurement":2427,
      -         "Stability_Index":112.6,
      -         "Dynamic_Allowance":0,
      -         "GPH":657.9,
      -         "TMF_Inshore":0.9475,
      -         "ILCWA":712.4,
      -         "TMF_Offshore":0.9351,
      -         "OSN":641.6,
      -         "CDL":7.481,
      -         "TN_Offshore_Low":0.9136,
      -         "TN_Offshore_Medium":1.1487,
      -         "TN_Offshore_High":1.2752,
      -         "TN_Inshore_Low":0.7117,
      -         "TN_Inshore_Medium":0.946,
      -         "TN_Inshore_High":1.0685,
      -         "TND_Offshore_Low":738.8,
      -         "TND_Offshore_Medium":587.6,
      -         "TND_Offshore_High":529.3,
      -         "TND_Inshore_Low":948.4,
      -         "TND_Inshore_Medium":713.5,
      -         "TND_Inshore_High":631.7,
      -         "Double_Handed_TOD":null,
      -         "Double_Handed_TOT":null,
      -         "OSN_Jibs":661,
      -         "TMF_Jibs":0.9077,
      -         "Dspl_Sailing":2955,
      -         "WSS":15.63,
      -         "Area_Main":30.18,
      -         "Area_Jib":23.35,
      -         "Area_Sym":49.95,
      -         "Area_ASym":49.37,
      -         "GPH_Jibs":679.9,
      -         "GPH_Jibs_TOT":0.8825,
      -         "GPH_DH":0,
      -         "GPH_DH_TOT":0,
      -         "Allowances":{ 
      -            "Beat":[ 
      -             { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":1036.9
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":864.7
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":780.4
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":754
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":743.7
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":739.4
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":737.3
      -               }
      -              
      -            ],
      -            "Run":[ 
      -            { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":1049.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":843
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":709.9
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":625.2
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":569.2
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":535.4
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":481.8
      -               }
      -            ],
      -            "BeatAngle":[ 
      -            	{ 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":40.8
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":40.8
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":38.2
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":37.2
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":36.8
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":36.8
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":37.5
      -               }
      -            ],
      -            "GybeAngle":[ 
      -            	{ 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":147.5
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":151
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":153.5
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":168.9
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":180
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":180
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":180
      -               }
      -            ],
      -            "WL":[ 
      -            	{ 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":1043
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":853.8
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":745.2
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":689.6
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":656.5
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":637.4
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":609.5
      -               }
      -              
      -            ],
      -            "CR":[ 
      -            	{ 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":890.9
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":726.2
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":639.4
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":589.6
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":559.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":538.7
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":511.2
      -               }
      -              
      -            ],
      -            "OC":[ 
      -            	{ 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":1043.6
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":805.8
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":678.6
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":609.8
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":572.4
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":541.2
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":486.8
      -               }
      -              
      -            ],
      -            "NS":[ 
      -            { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":929.4
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":753.6
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":660.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":606.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":572.9
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":551
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":522.5
      -               }
      -             
      -            ],
      -            "OL":[ 
      -            { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":991.7
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":813.8
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":724.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":679.6
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":654.6
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":638.3
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":611.9
      -               }
      -             ],
      -            "R52.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":561.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":562.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":563.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":564.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":565.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":566.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":567.1
      -               }
      -			   
      -            ],
      -            "R60.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":511.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":512.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":513.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":514.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":515.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":516.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":517.1
      -               }
      -			   
      -            ],
      -            "R75.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":521.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":522.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":523.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":524.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":525.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":526.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":527.1
      -               }
      -			   
      -            ],
      -            "R150.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":531.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":532.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":533.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":534.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":535.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":536.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":537.1
      -               }
      -			   
      -            ],
      -            "R90.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":541.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":542.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":543.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":544.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":545.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":546.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":547.1
      -               }
      -			   
      -            ],
      -            "R110.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":551.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":552.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":553.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":554.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":555.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":556.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":557.1
      -               }
      -			   
      -            ],
      -            "R120.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":571.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":572.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":573.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":574.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":575.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":576.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":577.1
      -               }
      -			   
      -            ],
      -            "R135.1":[ 
      -               { 
      -                  "twsInKts":7.5,
      -                  "allInSPNM":581.1
      -               },
      -               { 
      -                  "twsInKts":8.5,
      -                  "allInSPNM":582.1
      -               },
      -               { 
      -                  "twsInKts":9.5,
      -                  "allInSPNM":583.1
      -               },
      -               { 
      -                  "twsInKts":12.5,
      -                  "allInSPNM":584.1
      -               },
      -               { 
      -                  "twsInKts":16.5,
      -                  "allInSPNM":585.1
      -               },
      -               { 
      -                  "twsInKts":20.5,
      -                  "allInSPNM":586.1
      -               },
      -               { 
      -                  "twsInKts":30.5,
      -                  "allInSPNM":587.1
      -               }
      -			   
      -            ]
      -
      -         }
      +{
      +  "rms":[
      +    {
      +      "NatAuth":"GR",
      +      "BIN":"GR317",
      +      "CertNo":"A00073",
      +      "RefNo":"N\/A",
      +      "SailNo":"GRE-317",
      +      "YachtName":"AVANTES",
      +      "Class":"FIRST 31.7",
      +      "Builder":"BENETEAU",
      +      "Designer":"FINOT",
      +      "Address3":"I.O. HALKIDAS",
      +      "C_Type":"INTL",
      +      "Family":"ORC",
      +      "Division":"C",
      +      "IssueDate":"2020-02-05T12:54:23.484Z",
      +      "Age_Year":1998,
      +      "CrewWT":550,
      +      "LOA":9.49,
      +      "IMSL":9.003,
      +      "Draft":1.94,
      +      "MB":3.216,
      +      "Dspl_Measurement":3936,
      +      "Stability_Index":103.7,
      +      "Dynamic_Allowance":0.31,
      +      "GPH":678,
      +      "TMF_Inshore":0.9229,
      +      "ILCWA":731.4,
      +      "TMF_Offshore":0.9145,
      +      "OSN":656.1,
      +      "CDL":8.155,
      +      "TN_Offshore_Low":0.8578,
      +      "TN_Offshore_Medium":1.1292,
      +      "TN_Offshore_High":1.2751,
      +      "TN_Inshore_Low":0.6639,
      +      "TN_Inshore_Medium":0.9158,
      +      "TN_Inshore_High":1.0666,
      +      "TND_Offshore_Low":786.9,
      +      "TND_Offshore_Medium":597.8,
      +      "TND_Offshore_High":529.4,
      +      "TND_Inshore_Low":1016.7,
      +      "TND_Inshore_Medium":737.1,
      +      "TND_Inshore_High":632.9,
      +      "OSN_Jibs":688.6,
      +      "TMF_Jibs":0.8713,
      +      "Dspl_Sailing":4660,
      +      "WSS":22.41,
      +      "Area_Main":27.04,
      +      "Area_Jib":22.08,
      +      "Area_Sym":61.48,
      +      "Area_ASym":0,
      +      "GPH_Jibs":714.6,
      +      "GPH_Jibs_TOT":0.8396,
      +      "Allowances":{
      +        "WindSpeeds":[
      +          6.1,
      +          8.2,
      +          10.6,
      +          12.1,
      +          14.9,
      +          16.7,
      +          20.1
      +        ],
      +        "Beat":[
      +          1175.6,
      +          968.7,
      +          854,
      +          780.9,
      +          752,
      +          744.6,
      +          744.9
      +        ],
      +        "Run":[
      +          1074.7,
      +          847.7,
      +          716.4,
      +          628.3,
      +          566,
      +          526.9,
      +          480.1
      +        ],
      +        "BeatAngle":[
      +          43.5,
      +          41.6,
      +          40.5,
      +          39.8,
      +          39,
      +          38.6,
      +          39
      +        ],
      +        "GybeAngle":[
      +          146,
      +          150,
      +          153,
      +          158,
      +          178,
      +          180,
      +          180
      +        ],
      +        "WL":[
      +          1125.2,
      +          908.2,
      +          785.2,
      +          704.6,
      +          659,
      +          635.8,
      +          612.5
      +        ],
      +        "CR":[
      +          942.4,
      +          757.3,
      +          657.2,
      +          598.8,
      +          563,
      +          540.1,
      +          512.7
      +        ],
      +        "OC":[
      +          1120.1,
      +          852,
      +          706,
      +          616.2,
      +          571.7,
      +          539.8,
      +          487.7
      +        ],
      +        "NS":[
      +          1008.9,
      +          804,
      +          691.6,
      +          625.1,
      +          584.2,
      +          558,
      +          527.3
      +        ],
      +        "OL":[
      +          1077.4,
      +          874.1,
      +          761.4,
      +          694.6,
      +          658.2,
      +          638.7,
      +          615.2
      +        ],
      +        "R52":[
      +          767,
      +          645,
      +          577.3,
      +          544,
      +          530.1,
      +          525.8,
      +          525
      +        ],
      +        "R60":[
      +          725.4,
      +          613.2,
      +          556.7,
      +          530.3,
      +          516.8,
      +          510.9,
      +          508.9
      +        ],
      +        "R75":[
      +          697.1,
      +          589.3,
      +          541.9,
      +          519,
      +          502.9,
      +          490.9,
      +          480.5
      +        ],
      +        "R90":[
      +          694.3,
      +          577.7,
      +          526.9,
      +          505.5,
      +          495.9,
      +          481.7,
      +          454.3
      +        ],
      +        "R110":[
      +          688.6,
      +          568.4,
      +          520.4,
      +          493.9,
      +          469.8,
      +          450.8,
      +          430.3
      +        ],
      +        "R120":[
      +          710.9,
      +          583.7,
      +          526.8,
      +          498.8,
      +          473.3,
      +          448.4,
      +          409.9
      +        ],
      +        "R135":[
      +          788.3,
      +          640.5,
      +          556.1,
      +          517.8,
      +          492.5,
      +          466.8,
      +          416.9
      +        ],
      +        "R150":[
      +          930.8,
      +          734.2,
      +          621.9,
      +          551.9,
      +          517.7,
      +          494,
      +          446.5
      +        ]
             }
      -
      -   ]
      -}
      \ No newline at end of file
      +    }
      +  ]
      +}
      
      From 1efdff0a1546d35759c8605739a6ba5acab5d904 Mon Sep 17 00:00:00 2001
      From: Usman Ali 
      Date: Fri, 7 Feb 2020 15:50:42 +0500
      Subject: [PATCH 47/85] Changing new format orc json file to support custom TWA
      
      ---
       .../resources/rms/newFormatCertificate.json                   | 4 ++--
       1 file changed, 2 insertions(+), 2 deletions(-)
      
      diff --git a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      index 87030c91d15..f9c584edab4 100644
      --- a/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      +++ b/java/com.sap.sailing.server.gateway.serialization.test/resources/rms/newFormatCertificate.json
      @@ -143,7 +143,7 @@
                 638.7,
                 615.2
               ],
      -        "R52":[
      +        "R52.7":[
                 767,
                 645,
                 577.3,
      @@ -152,7 +152,7 @@
                 525.8,
                 525
               ],
      -        "R60":[
      +        "R64.4":[
                 725.4,
                 613.2,
                 556.7,
      
      From 4527bf6fc0a50caab630b28066ed7db4056c2977 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Sun, 9 Feb 2020 23:44:07 +0100
      Subject: [PATCH 48/85] changed from cloud.docker.com to hub.docker.com for
       Docker scripts
      
      ---
       docker/deleteTag.sh   | 4 ++--
       docker/obtainToken.sh | 2 +-
       docker/showTags.sh    | 2 +-
       3 files changed, 4 insertions(+), 4 deletions(-)
      
      diff --git a/docker/deleteTag.sh b/docker/deleteTag.sh
      index 4a7b706e3f8..f08a3509c3a 100755
      --- a/docker/deleteTag.sh
      +++ b/docker/deleteTag.sh
      @@ -27,7 +27,7 @@ shift
       echo "Deleting tags $* in repository ${REPO}..."
       
       # delete images and/or tags
      -IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://cloud.docker.com/v2/repositories/${REPO}/tags/?page_size=1000 | jq -r '.results|.[]|.name')
      +IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${REPO}/tags/?page_size=1000 | jq -r '.results|.[]|.name')
       for j in ${IMAGE_TAGS}
       do
         # echo "Checking tag $j"
      @@ -39,7 +39,7 @@ do
             if [[ $j =~ $k ]]; then
       	found=1
       	echo -n "  - ${j} ... "
      -	curl -X DELETE -s -H "Authorization: JWT ${TOKEN}" https://cloud.docker.com/v2/repositories/${REPO}/tags/${j}/
      +	curl -X DELETE -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${REPO}/tags/${j}/
       	echo "DELETED ($?)"
             fi
           fi
      diff --git a/docker/obtainToken.sh b/docker/obtainToken.sh
      index 405fe24ac55..81b1b664240 100755
      --- a/docker/obtainToken.sh
      +++ b/docker/obtainToken.sh
      @@ -14,6 +14,6 @@ echo
       
       # get token
       echo "Retrieving token ..."
      -TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://cloud.docker.com/v2/users/login/ | jq -r .token)
      +TOKEN=$(curl -s -H "Content-Type: application/json" -X POST -d '{"username": "'${UNAME}'", "password": "'${UPASS}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)
       
       echo "$TOKEN" >"${TOKEN_FILE}"
      diff --git a/docker/showTags.sh b/docker/showTags.sh
      index 5c2857bc79d..31f8286b959 100755
      --- a/docker/showTags.sh
      +++ b/docker/showTags.sh
      @@ -23,7 +23,7 @@ shift
       
       # -------
       
      -IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://cloud.docker.com/v2/repositories/${REPO}/tags/?page_size=1000 | jq -r '.results|.[]|.name')
      +IMAGE_TAGS=$(curl -s -H "Authorization: JWT ${TOKEN}" https://hub.docker.com/v2/repositories/${REPO}/tags/?page_size=1000 | jq -r '.results|.[]|.name')
       for j in ${IMAGE_TAGS}
       do
         if [ "$#" = "0" ]; then
      
      From f2939ea892eb6778913dcb32ae799d8df47b3124 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Mon, 10 Feb 2020 09:34:35 +0100
      Subject: [PATCH 49/85] bug5200: added null-check for the case that ToT/ToD
       regatta has no competitors
      
      ---
       .../domain/ranking/AbstractRankingMetric.java |  7 ++-
       .../NonPerformanceCurveRankingMetric.java     | 56 ++++++++++---------
       2 files changed, 33 insertions(+), 30 deletions(-)
      
      diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
      index dcba69f0d8e..95fc5c4b547 100755
      --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
      +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
      @@ -6,6 +6,7 @@ import java.util.HashMap;
       import java.util.Iterator;
       import java.util.List;
       import java.util.Map;
      +import java.util.Optional;
       import java.util.concurrent.ConcurrentHashMap;
       import java.util.concurrent.ConcurrentMap;
       import java.util.function.Function;
      @@ -238,10 +239,10 @@ public abstract class AbstractRankingMetric implements RankingMetric {
       
           protected Competitor getCompetitorFarthestAhead(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
               Comparator oneDesignComparator = getWindwardDistanceTraveledComparator(timePoint, cache);
      -        Competitor competitorFarthestAhead = StreamSupport
      +        Optional competitorFarthestAhead = StreamSupport
                       .stream(getCompetitors().spliterator(), /* parallel */true).
      -                sorted(oneDesignComparator).findFirst().get();
      -        return competitorFarthestAhead;
      +                sorted(oneDesignComparator).findFirst();
      +        return competitorFarthestAhead.orElse(null);
           }
           
           /**
      diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
      index 97179fbe809..986b23aa9af 100755
      --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
      +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/NonPerformanceCurveRankingMetric.java
      @@ -79,33 +79,35 @@ public abstract class NonPerformanceCurveRankingMetric extends AbstractRankingMe
           public RankingMetric.RankingInfo getRankingInfo(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
               Map result = new HashMap<>();
               Competitor competitorFarthestAhead = getCompetitorFarthestAhead(timePoint, cache);
      -        final Distance totalWindwardDistanceTraveled = getWindwardDistanceTraveled(competitorFarthestAhead, timePoint, cache);
      -        final TimePoint startOfRace = getTrackedRace().getStartOfRace();
      -        if (startOfRace != null) {
      -            final Duration actualRaceDuration = startOfRace.until(timePoint);
      -            for (Competitor competitor : getCompetitors()) {
      -                // TODO bug5110: we cannot compute the following if at timePoint the position of either of the two competitors involved is unknown; we can, however, do this if timePoint is after the two finish mark passings, or if the competitorFarthestAhead has already finished at timePoint and the position of "competitor" is known.
      -                final Duration predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead = getPredictedDurationToReachWindwardPositionOf(
      -                        competitor, competitorFarthestAhead, timePoint, cache);
      -                final Duration totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead = predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead == null ? null
      -                        : actualRaceDuration.plus(predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead);
      -                final Duration calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead = totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead == null ? null
      -                        : getCalculatedTime(
      -                                competitor,
      -                                () -> getTrackedRace().getTrackedLeg(competitorFarthestAhead, timePoint).getLeg(),
      -                                () -> getTrackedRace().getTrack(competitorFarthestAhead).getEstimatedPosition(
      -                                        timePoint, /* extrapolate */true),
      -                                totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead,
      -                                totalWindwardDistanceTraveled);
      -                final Duration calculatedTime = getCalculatedTime(competitor,
      -                        () -> getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), () -> getTrackedRace()
      -                                .getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolated */true),
      -                        getTrackedRace().getTimeSailedSinceRaceStart(competitor, timePoint), totalWindwardDistanceTraveled);
      -                RankingMetric.CompetitorRankingInfo rankingInfo = new CompetitorRankingInfoImpl(timePoint, competitor,
      -                        getWindwardDistanceTraveled(competitor, timePoint, cache), actualRaceDuration, calculatedTime,
      -                        predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead,
      -                        calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead);
      -                result.put(competitor, rankingInfo);
      +        if (competitorFarthestAhead != null) {
      +            final Distance totalWindwardDistanceTraveled = getWindwardDistanceTraveled(competitorFarthestAhead, timePoint, cache);
      +            final TimePoint startOfRace = getTrackedRace().getStartOfRace();
      +            if (startOfRace != null) {
      +                final Duration actualRaceDuration = startOfRace.until(timePoint);
      +                for (Competitor competitor : getCompetitors()) {
      +                    // TODO bug5110: we cannot compute the following if at timePoint the position of either of the two competitors involved is unknown; we can, however, do this if timePoint is after the two finish mark passings, or if the competitorFarthestAhead has already finished at timePoint and the position of "competitor" is known.
      +                    final Duration predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead = getPredictedDurationToReachWindwardPositionOf(
      +                            competitor, competitorFarthestAhead, timePoint, cache);
      +                    final Duration totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead = predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead == null ? null
      +                            : actualRaceDuration.plus(predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead);
      +                    final Duration calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead = totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead == null ? null
      +                            : getCalculatedTime(
      +                                    competitor,
      +                                    () -> getTrackedRace().getTrackedLeg(competitorFarthestAhead, timePoint).getLeg(),
      +                                    () -> getTrackedRace().getTrack(competitorFarthestAhead).getEstimatedPosition(
      +                                            timePoint, /* extrapolate */true),
      +                                    totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead,
      +                                    totalWindwardDistanceTraveled);
      +                    final Duration calculatedTime = getCalculatedTime(competitor,
      +                            () -> getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), () -> getTrackedRace()
      +                                    .getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolated */true),
      +                            getTrackedRace().getTimeSailedSinceRaceStart(competitor, timePoint), totalWindwardDistanceTraveled);
      +                    RankingMetric.CompetitorRankingInfo rankingInfo = new CompetitorRankingInfoImpl(timePoint, competitor,
      +                            getWindwardDistanceTraveled(competitor, timePoint, cache), actualRaceDuration, calculatedTime,
      +                            predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead,
      +                            calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead);
      +                    result.put(competitor, rankingInfo);
      +                }
                   }
               }
               return new NonPerformanceCurveRankingInfoImpl(timePoint, result, competitorFarthestAhead);
      
      From 1e1c2d026901dde0d98ec70f085182060cf4bec3 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Mon, 10 Feb 2020 12:00:58 +0100
      Subject: [PATCH 50/85] consolidated imports to avoid warning
      
      ---
       .../orc/impl/ORCCertificateImpl_CustomFieldSerializer.java       | 1 -
       1 file changed, 1 deletion(-)
      
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java
      index f9cd064bc8c..e966a0e401b 100755
      --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java
      +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java
      @@ -1,6 +1,5 @@
       package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.orc.impl;
       
      -import java.util.ArrayList;
       import java.util.Arrays;
       import java.util.HashMap;
       import java.util.List;
      
      From 0383ee7dc5e9d374a6f17b409884382a43e94250 Mon Sep 17 00:00:00 2001
      From: Axel Uhl 
      Date: Mon, 10 Feb 2020 12:17:49 +0100
      Subject: [PATCH 51/85] move TWS/TWA default bin definitions up to
       ORCCertificate, added Javadoc and adjusted to coding conventions
      
      ---
       .../domain/common/orc/ORCCertificate.java     | 58 +++++++++++++++++--
       .../common/orc/impl/ORCCertificateImpl.java   | 45 +++++---------
       .../orc/TestORCCertificateImporterJSON.java   |  8 +--
       .../orc/TestORCCertificateImporterRMS.java    |  8 +--
       .../domain/orc/TestORCPerformanceCurve.java   |  8 +--
       .../impl/ORCCertificatesCollectionJSON.java   |  2 +-
       .../impl/ORCCertificatesCollectionRMS.java    |  4 +-
       .../orc/impl/ORCPerformanceCurveImpl.java     | 12 ++--
       ...CertificateImpl_CustomFieldSerializer.java |  9 +--
       .../impl/ORCCertificateJsonDeserializer.java  |  8 +--
       .../impl/ORCCertificateJsonSerializer.java    | 31 +++++-----
       11 files changed, 112 insertions(+), 81 deletions(-)
      
      diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java
      index 2ccafb5a5cc..61320a5419b 100755
      --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java
      +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/ORCCertificate.java
      @@ -3,33 +3,67 @@ package com.sap.sailing.domain.common.orc;
       import java.io.Serializable;
       import java.util.Map;
       
      +import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
       import com.sap.sse.common.Bearing;
       import com.sap.sse.common.Distance;
       import com.sap.sse.common.Duration;
       import com.sap.sse.common.Speed;
       import com.sap.sse.common.TimePoint;
       import com.sap.sse.common.WithID;
      +import com.sap.sse.common.impl.DegreeBearingImpl;
       
       /**
        * Represents semantically a real ORC certificate for a {@link Competitor}, which is used to rate different type of
      - * boats for different insohre and offshore race conditions.
      + * boats for different inshore and offshore race conditions.
        * 

      * An ORC certificate is issued by the "Member National Authorities" of World Sailing and are available for insight at * https://www.orc.org/index.asp . Other information about the whole scoring system and different variants are available * too. *

      * One implementing class provides all necessary functionalities to score the Competitors with a choosen - * {@link RankingMetric}.

      + * {@link RankingMetric}. + *

      * - * The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that - * contains the concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo} - * and the {@code BIN} fields, as provided as single fields in the JSON representation, and as provided - * in the concatenated form in the {@code NATCERTN.FILE_ID} column in the RMS format. + * The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that contains the + * concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo} and the {@code BIN} + * fields, as provided as single fields in the JSON representation, and as provided in the concatenated form in the + * {@code NATCERTN.FILE_ID} column in the RMS format. + *

      + * + * The certificate contains time allowances (or, conversely, speed predictions) for a combination of true wind speeds + * (TWS) and true wind angles (TWA). There are defaults for the values used for the true wind speeds and the true wind + * angles, forming a regular, fully populated matrix of allowances / speed predictions. These defaults can be found in + * {@link #ALLOWANCES_TRUE_WIND_SPEEDS} and {@link #ALLOWANCES_TRUE_WIND_SPEEDS}. + *

      + * + * Certificates may also specify non-default values for the slots or "bins" of the matrix that describes the allowances + * / speed predictions. As for the default values, the allowances matrix has to be fully populated. The values in the + * TWS and TWA arrays are expected to be increasing monotonously. * * @author Daniel Lisunkin (i505543) * */ public interface ORCCertificate extends WithID, Serializable { + /** + * Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore + * racing congress. The speeds occur in the array in ascending order. + * + * There are references in the persistance module. If the values change, there will be an adjustment needed + * in {@link MongoObjectFactoryImpl.speedToKnotsString}. + */ + Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8), + new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16), + new KnotSpeedImpl(20) }; + /** + * Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the + * offshore racing congress. The angles occur in the array in ascending order. + * + * There are references in the persistance module. If the values change, there will be an adjustment needed + * in {@link MongoObjectFactoryImpl.bearingToDegreeString}. + */ + Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60), + new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110), + new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) }; /** * As the ID of an ORC certificate we use the concatenation (without intermediate white space) of the * {@code NatAuth}, the {@code CertNo} and the {@code BIN} fields, as provided as single fields in the JSON @@ -129,6 +163,18 @@ public interface ORCCertificate extends WithID, Serializable { String getBoatName(); + /** + * @return the true wind speeds used for the matrix returned by + * {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Speed} values returned by this + * method form the key set of the map returned by {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. + */ Speed[] getTrueWindSpeeds(); + + /** + * @return the true wind angles used for the matrix returned by + * {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Bearing} values returned by this + * method form the key set of all the maps returned as values in the map returned by + * {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. + */ Bearing[] getTrueWindAngles(); } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java index 98c51e85c92..e9a59f30b09 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java @@ -3,7 +3,6 @@ package com.sap.sailing.domain.common.orc.impl; import java.util.Collections; import java.util.Map; -import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.NauticalMileDistance; import com.sap.sailing.domain.common.orc.ORCCertificate; import com.sap.sse.common.Bearing; @@ -12,7 +11,6 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; -import com.sap.sse.common.impl.DegreeBearingImpl; /** * Holds an ORC boat certificate including various of the metrics found in it. Note that multiple different certificates @@ -28,28 +26,6 @@ public class ORCCertificateImpl implements ORCCertificate { private final String idConsistingOfNatAuthCertNoAndBIN; - /** - * Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore - * racing congress. The speeds occur in the array in ascending order. - * - * There are references in the persistance module. If the values change, there will be an adjustment needed - * in {@link MongoObjectFactoryImpl.speedToKnotsString}. - */ - public static final Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8), - new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16), - new KnotSpeedImpl(20) }; - - /** - * Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the - * offshore racing congress. The angles occur in the array in ascending order. - * - * There are references in the persistance module. If the values change, there will be an adjustment needed - * in {@link MongoObjectFactoryImpl.bearingToDegreeString}. - */ - public static final Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60), - new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110), - new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) }; - public final static Distance NAUTICAL_MILE = new NauticalMileDistance(1); private final String sailNumber; @@ -113,6 +89,9 @@ public class ORCCertificateImpl implements ORCCertificate { private final Speed[] allowancesTrueWindSpeeds; private final Bearing[] allowancesTrueWindAngles; + /** + * Can be used to specify non-default TWS/TWA values for the time allowances / speed predictions. + */ public ORCCertificateImpl(Speed[] dynamicTrueWindSpeed, Bearing[] dynamicTrueWindAngle, String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, Distance length, Duration gph, Double cdl, TimePoint issueDate, @@ -147,6 +126,12 @@ public class ORCCertificateImpl implements ORCCertificate { this.circularRandomSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(circularRandomSpeedPredictionsPerTrueWindSpeed); this.nonSpinnakerSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(nonSpinnakerSpeedPredictionsPerTrueWindSpeed); } + + /** + * Uses the default TWS/TWA values from {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS} and + * {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}, respectively, for the matrix of time allowances + * or, conversely, the speed predictions. + */ public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, Distance length, Duration gph, @@ -160,12 +145,12 @@ public class ORCCertificateImpl implements ORCCertificate { Map longDistanceSpeedPredictionsPerTrueWindSpeed, Map circularRandomSpeedPredictionsPerTrueWindSpeed, Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { - this(ALLOWANCES_TRUE_WIND_SPEEDS,ALLOWANCES_TRUE_WIND_ANGLES,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatClassName, length, gph, cdl, issueDate, - velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, - beatAllowancePerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, - runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionsPerTrueWindSpeed, - longDistanceSpeedPredictionsPerTrueWindSpeed, circularRandomSpeedPredictionsPerTrueWindSpeed, - nonSpinnakerSpeedPredictionsPerTrueWindSpeed); + this(ALLOWANCES_TRUE_WIND_SPEEDS, ALLOWANCES_TRUE_WIND_ANGLES, idConsistingOfNatAuthCertNoAndBIN, sailnumber, + boatName, boatClassName, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, + beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles, + runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed, + windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed, + circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed); } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterJSON.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterJSON.java index 59dfc372fee..d6b290ed8b4 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterJSON.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterJSON.java @@ -37,9 +37,9 @@ public class TestORCCertificateImporterJSON { assertNotNull(moana); assertEquals(539.1, swan .getGPHInSecondsToTheMile(), 0.0000001); assertEquals(490.4, moana.getGPHInSecondsToTheMile(), 0.0000001); - assertEquals(862.2, swan .getWindwardLeewardSpeedPrediction().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(788.2, moana.getWindwardLeewardSpeedPrediction().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(861.0, swan .getLongDistanceSpeedPredictions().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(787.2, moana.getLongDistanceSpeedPredictions().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(862.2, swan .getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(788.2, moana.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(861.0, swan .getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(787.2, moana.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterRMS.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterRMS.java index 04fb90f60d6..c4f4620dcfd 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterRMS.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCCertificateImporterRMS.java @@ -37,10 +37,10 @@ public class TestORCCertificateImporterRMS { assertNotNull(moana); assertEquals(539.1, swan .getGPHInSecondsToTheMile(), 0.0000001); assertEquals(490.4, moana.getGPHInSecondsToTheMile(), 0.0000001); - assertEquals(862.2, swan .getWindwardLeewardSpeedPrediction().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(788.2, moana.getWindwardLeewardSpeedPrediction().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(861.0, swan .getLongDistanceSpeedPredictions().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); - assertEquals(787.2, moana.getLongDistanceSpeedPredictions().get(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(862.2, swan .getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(788.2, moana.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(861.0, swan .getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); + assertEquals(787.2, moana.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificateImpl.NAUTICAL_MILE).asSeconds(), 0.1); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index adb7bfc2944..94bce72f709 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -120,13 +120,13 @@ public class TestORCPerformanceCurve { final ORCPerformanceCurveCourse course = new ORCPerformanceCurveCourseImpl(Arrays.asList(new ORCPerformanceCurveLegImpl(ORCCertificateImpl.NAUTICAL_MILE, new DegreeBearingImpl(twa)))); final ORCPerformanceCurve performanceCurve = new ORCPerformanceCurveImpl(certificate, course); for (int i=0; i legs = new ArrayList<>(); legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.0), new DegreeBearingImpl(0))); legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.0), new DegreeBearingImpl(30))); @@ -142,14 +142,14 @@ public class TestORCPerformanceCurve { private void testAllowancePerCourse(ORCPerformanceCurve performanceCurve, double... allowancePerNauticalMileInSeconds) throws ArgumentOutsideDomainException { for (int i=0; i longDistanceSpeedPredictionPerTrueWindSpeed = new HashMap<>(); final Map circularRandomSpeedPredictionPerTrueWindSpeed = new HashMap<>(); final Map nonSpinnakerSpeedPredictionPerTrueWindSpeed = new HashMap<>(); - for (Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + for (Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { String windSpeed = Integer.toString((int) tws.getKnots()); String beatAngleKey = BEAT_ANGLE + windSpeed; String beatAllowanceKey = BEAT_ALLOWANCE + windSpeed; @@ -147,7 +147,7 @@ public class ORCCertificatesCollectionRMS extends AbstractORCCertificatesCollect nonSpinnakerSpeedPredictionPerTrueWindSpeed.put(tws, ORCCertificateImpl.NAUTICAL_MILE.inTime( new SecondsDurationImpl(Double.parseDouble(certificateValues.getValue(nonSpinnakerKey))))); Map velocityPredictionPerTrueWindAngle = new HashMap<>(); - for (Bearing twa : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES ) { + for (Bearing twa : ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES ) { String twaCoursesKey = TWA_COURSES + Integer.toString((int) twa.getDegrees()) + windSpeed; velocityPredictionPerTrueWindAngle.put(twa, ORCCertificateImpl.NAUTICAL_MILE.inTime( new SecondsDurationImpl(Double.parseDouble(certificateValues.getValue(twaCoursesKey))))); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java index cedeae527da..05f52de9e46 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java @@ -142,7 +142,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv for (final ORCPerformanceCurveLeg leg : course.getLegs()) { allowancesPerLeg.put(leg, createAllowancePerLeg(leg, certificate)); } - for (final Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { Duration allowancePerTws = new SecondsDurationImpl(0); for (final ORCPerformanceCurveLeg leg : course.getLegs()) { allowancePerTws = allowancePerTws.plus(allowancesPerLeg.get(leg).get(tws)); @@ -251,14 +251,14 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv @Override public Speed getImpliedWind(Duration durationToCompleteCourse) throws MaxIterationsExceededException, FunctionEvaluationException{ final Speed averageSpeedOnCourse = getCourse().getTotalLength().inTime(durationToCompleteCourse); - final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS).mapToDouble + final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS).mapToDouble (tws->{ return functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse.value(tws.getKnots()); }).toArray(); final Speed result; // Corner cases for Allowance > Allowance(20kt) or Allowance < Allowance(6kt) if (averageSpeedOnCourse.getKnots() >= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed.length-1]) { - result = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS.length-1]; + result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS.length-1]; } else if (averageSpeedOnCourse.equals(Speed.NULL) || averageSpeedOnCourse.getKnots() <= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[0]) { - result = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS[0]; + result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]; } else { // find the polynomial splined function that produces the durationToCompleteCourse within its validity range int i = 1; // skip the auxiliary spline segment from (0.0, 0.0) to (6.0, ...) @@ -296,7 +296,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv Bearing trueWindAngle) throws FunctionEvaluationException, IllegalArgumentException { return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances,beatAngles,beatVMGPredictionPerTrueWindSpeed, - runAngles,runVMGPredictionPerTrueWindSpeed,trueWindSpeed,trueWindAngle, ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES); + runAngles,runVMGPredictionPerTrueWindSpeed,trueWindSpeed,trueWindAngle, ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES); } public Speed getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(Map> twaAllowances, @@ -341,7 +341,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv */ public LinkedHashMap getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException { final LinkedHashMap result = new LinkedHashMap<>(); - for (final Speed tws : ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS) { + for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { result.put(tws, getAllowancePerCourse(tws)); } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java index e966a0e401b..33fb5e32efc 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java @@ -9,6 +9,7 @@ import com.google.gwt.user.client.rpc.CustomFieldSerializer; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; +import com.sap.sailing.domain.common.orc.ORCCertificate; import com.sap.sailing.domain.common.orc.impl.ORCCertificateImpl; import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; @@ -24,7 +25,7 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali serialize(streamWriter, instance); } - public static void serialize(SerializationStreamWriter streamWriter, ORCCertificateImpl instance) + public static void serialize(SerializationStreamWriter streamWriter, ORCCertificate instance) throws SerializationException { streamWriter.writeString(instance.getId()); streamWriter.writeString(instance.getSailNumber()); @@ -115,7 +116,7 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali trueWIndSpeedList.toArray(trueWindSpeeds); } } catch (SerializationException e) { - trueWindSpeeds = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_SPEEDS; + trueWindSpeeds = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS; } return trueWindSpeeds; } @@ -131,7 +132,7 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali trueWIndAnglesList.toArray(trueWindAngles); } } catch (SerializationException e) { - trueWindAngles = ORCCertificateImpl.ALLOWANCES_TRUE_WIND_ANGLES; + trueWindAngles = ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES; } return trueWindAngles; } @@ -142,7 +143,7 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali deserialize(streamReader, instance); } - public static void deserialize(SerializationStreamReader streamReader, ORCCertificateImpl instance) { + public static void deserialize(SerializationStreamReader streamReader, ORCCertificate instance) { // Done by instantiateInstance } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java index 407158ec067..91389b7e0c1 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java @@ -113,10 +113,10 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer Date: Mon, 10 Feb 2020 12:23:44 +0100 Subject: [PATCH 52/85] improved Javadocs --- .../common/orc/impl/ORCCertificateImpl.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java index e9a59f30b09..8f6f857b189 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl.java @@ -36,7 +36,16 @@ public class ORCCertificateImpl implements ORCCertificate { private final Double cdl; private final TimePoint issueDate; - // TODO add meaningful Javadoc + /** + * The "core" of the certificate for performance curve scoring; the values in the map tell how fast the boat + * described by this certificate is expected to sail for the true wind speed (TWS) provided by the key, and the true + * wind angle (TWA) provided by the value map's key. This is the reciprocal of the duration it takes the boat to + * sail a certain distance. + *

      + * + * The keys are the speeds as provided by {@link #allowancesTrueWindSpeeds}; the keys of the value maps are the + * angles as provided by {@link #allowancesTrueWindAngles}. + */ private final Map> velocityPredictionPerTrueWindSpeedAndAngle; /** @@ -91,8 +100,17 @@ public class ORCCertificateImpl implements ORCCertificate { /** * Can be used to specify non-default TWS/TWA values for the time allowances / speed predictions. + * + * @param allowancesTrueWindSpeeds + * a monotonously-increasing sequence of true wind speed (TWS) values for the matrix of time allowances / + * velocity predictions for this certificate. See also + * {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS}. + * @param allowancesTrueWindAngles + * a monotonously-increasing sequence of true wind angle (TWA) values for the matrix of time allowances / + * velocity predictions for this certificate. See also + * {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}. */ - public ORCCertificateImpl(Speed[] dynamicTrueWindSpeed, Bearing[] dynamicTrueWindAngle, + public ORCCertificateImpl(Speed[] allowancesTrueWindSpeeds, Bearing[] allowancesTrueWindAngles, String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName, Distance length, Duration gph, Double cdl, TimePoint issueDate, Map> velocityPredictionsPerTrueWindSpeedAndAngle, Map beatAngles, @@ -103,8 +121,8 @@ public class ORCCertificateImpl implements ORCCertificate { Map longDistanceSpeedPredictionsPerTrueWindSpeed, Map circularRandomSpeedPredictionsPerTrueWindSpeed, Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed) { - this.allowancesTrueWindAngles = dynamicTrueWindAngle; - this.allowancesTrueWindSpeeds = dynamicTrueWindSpeed; + this.allowancesTrueWindAngles = allowancesTrueWindAngles; + this.allowancesTrueWindSpeeds = allowancesTrueWindSpeeds; this.idConsistingOfNatAuthCertNoAndBIN = idConsistingOfNatAuthCertNoAndBIN; this.sailNumber = sailnumber; this.boatName = boatName; From dba2f7683fa63c17d01f702305bbc48334766645 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 10 Feb 2020 12:43:16 +0100 Subject: [PATCH 53/85] avoid code redundancies in ORCCertificateJsonDeserializer --- .../impl/ORCCertificateJsonDeserializer.java | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java index 91389b7e0c1..6abb78cef24 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java @@ -1,7 +1,10 @@ package com.sap.sailing.server.gateway.deserialization.racelog.impl; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -26,7 +29,6 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer longDistanceSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map circularRandomSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map nonSpinnakerSpeedPredictionPerTrueWindSpeed = new HashMap<>(); - Speed[] trueWindSpeeds = convertJsonArrayToTrueWindSpeedOrReturnDefault(json); - Bearing[] trueWindAngles = convertJsonArrayToTrueWindAngleOrReturnDefault(json); + Speed[] trueWindSpeeds = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_ANGLE), + ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, speedInKnots->new KnotSpeedImpl(speedInKnots)); + Bearing[] trueWindAngles = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEED), + ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, twaInTrueDegrees->new DegreeBearingImpl(twaInTrueDegrees)); for (Speed tws : trueWindSpeeds) { String twsKey = ORCCertificateJsonSerializer.speedToKnotsString(tws); beatAngles.put(tws, new DegreeBearingImpl( @@ -88,7 +92,6 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer velocityPredictionAtCurrentTrueWindSpeedPerTrueWindAngle = new HashMap<>(); for (Bearing twa : trueWindAngles) { String twaKey = ORCCertificateJsonSerializer.bearingToDegreeString(twa); @@ -110,35 +113,21 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer T[] convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(final Object windAnglesObject, + final T[] defaults, final Function constructor) { + final T[] result; + final JSONArray windAnglesJsonArray; + if (windAnglesObject == null || !(windAnglesObject instanceof JSONArray) || (windAnglesJsonArray=(JSONArray) windAnglesObject).isEmpty()) { + result = defaults; + } else { + final List resultList = new ArrayList<>(); + for (final Object number : windAnglesJsonArray) { + resultList.add(constructor.apply((Double) number)); + } + @SuppressWarnings("unchecked") + final T[] tArray = (T[]) resultList.toArray(); + result = tArray; } - - Bearing[] trueWindAngles = new Bearing[windAnglesJsonArray.size()]; - for (int count = 0; count < windAnglesJsonArray.size(); count++) { - trueWindAngles[count] = new DegreeBearingImpl((Double) windAnglesJsonArray.get(count)); - } - return trueWindAngles; - } - - private Speed[] convertJsonArrayToTrueWindSpeedOrReturnDefault(JSONObject json) { - Object windSpeedsObject = json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEED); - if (windSpeedsObject == null || !(windSpeedsObject instanceof JSONArray)) - return ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS; - JSONArray windSpeedsJsonArray = (JSONArray) windSpeedsObject; - if (windSpeedsJsonArray == null || windSpeedsJsonArray.size() == 0) { - return ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS; - } - - Speed[] trueWindSpeeds = new Speed[windSpeedsJsonArray.size()]; - for (int count = 0; count < windSpeedsJsonArray.size(); count++) { - trueWindSpeeds[count] = new KnotSpeedImpl((Double) windSpeedsJsonArray.get(count)); - } - return trueWindSpeeds; + return result; } } From 7854c5f3ae5b9953000bffea8351f4360f7369a9 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 10 Feb 2020 12:55:47 +0100 Subject: [PATCH 54/85] not serializing redundant copies of the default TWA/TWS "bins" in each certificate; code formatting and eliminating code redundancy in ORCCertificateJsonSerializer --- .../impl/ORCCertificateJsonDeserializer.java | 4 +-- .../impl/ORCCertificateJsonSerializer.java | 32 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java index 6abb78cef24..326e69fd9fa 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java @@ -50,9 +50,9 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer longDistanceSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map circularRandomSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map nonSpinnakerSpeedPredictionPerTrueWindSpeed = new HashMap<>(); - Speed[] trueWindSpeeds = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_ANGLE), + Speed[] trueWindSpeeds = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_ANGLES_IN_TRUE_DEGREES), ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, speedInKnots->new KnotSpeedImpl(speedInKnots)); - Bearing[] trueWindAngles = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEED), + Bearing[] trueWindAngles = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEEDS_IN_KNOTS), ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, twaInTrueDegrees->new DegreeBearingImpl(twaInTrueDegrees)); for (Speed tws : trueWindSpeeds) { String twsKey = ORCCertificateJsonSerializer.speedToKnotsString(tws); diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index 7077871064f..0fdc6bd31a2 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -1,6 +1,7 @@ package com.sap.sailing.server.gateway.serialization.racelog.impl; import java.text.MessageFormat; +import java.util.function.Function; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -48,8 +49,8 @@ public class ORCCertificateJsonSerializer implements JsonSerializertwa.getDegrees(), certificate.getTrueWindAngles())); + } + if (certificate.getTrueWindSpeeds() != ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { + result.put(ORC_CERTIFICATE_TRUE_WIND_SPEEDS_IN_KNOTS, convertObjectsToJsonArrayOfDoubles(tws->tws.getKnots(), certificate.getTrueWindSpeeds())); + } return result; } - private JSONArray convertTrueWindSpeedsToJsonArray(ORCCertificate certificate) { - JSONArray jsonArray = new JSONArray(); - for( Speed speed : certificate.getTrueWindSpeeds()) { - jsonArray.add(speed.getKnots()); - } - return jsonArray; - } - - private JSONArray convertTrueWindAnglesToJsonArray(ORCCertificate certificate) { - JSONArray jsonArray = new JSONArray(); - for( Bearing bearing : certificate.getTrueWindAngles()) { - jsonArray.add(bearing.getDegrees()); + @SafeVarargs + private final JSONArray convertObjectsToJsonArrayOfDoubles(Function converterToDouble, T... objects) { + final JSONArray jsonArray = new JSONArray(); + for (T t : objects) { + jsonArray.add(converterToDouble.apply(t)); } return jsonArray; } From 1bc2f8099fa8baf3adc864d06a5958f82afde3d6 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 09:46:47 +0100 Subject: [PATCH 55/85] cleaned up ORC certificate (de-)serialization tests and strengthened verdicts --- .../impl/ORCCertificateJsonDeserializer.java | 17 ++++--- .../impl/ORCCertificateJsonSerializer.java | 4 +- .../ORCCertificateJsonDeserializerTest.java | 23 +++++++-- .../ORCCertificateJsonSerializerTest.java | 48 ------------------- 4 files changed, 30 insertions(+), 62 deletions(-) delete mode 100644 java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java index 326e69fd9fa..a87ef6a3a58 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/ORCCertificateJsonDeserializer.java @@ -50,10 +50,10 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer longDistanceSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map circularRandomSpeedPredictionPerTrueWindSpeed = new HashMap<>(); Map nonSpinnakerSpeedPredictionPerTrueWindSpeed = new HashMap<>(); - Speed[] trueWindSpeeds = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_ANGLES_IN_TRUE_DEGREES), - ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, speedInKnots->new KnotSpeedImpl(speedInKnots)); - Bearing[] trueWindAngles = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEEDS_IN_KNOTS), - ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, twaInTrueDegrees->new DegreeBearingImpl(twaInTrueDegrees)); + Speed[] trueWindSpeeds = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_SPEEDS_IN_KNOTS), + ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, speedInKnots->new KnotSpeedImpl(speedInKnots), new Speed[0]); + Bearing[] trueWindAngles = convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(json.get(ORCCertificateJsonSerializer.ORC_CERTIFICATE_TRUE_WIND_ANGLES_IN_TRUE_DEGREES), + ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, twaInTrueDegrees->new DegreeBearingImpl(twaInTrueDegrees), new Bearing[0]); for (Speed tws : trueWindSpeeds) { String twsKey = ORCCertificateJsonSerializer.speedToKnotsString(tws); beatAngles.put(tws, new DegreeBearingImpl( @@ -113,19 +113,18 @@ public class ORCCertificateJsonDeserializer implements JsonDeserializer T[] convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(final Object windAnglesObject, - final T[] defaults, final Function constructor) { + private T[] convertJsonArrayOfDoublesToArrayOfObjectsOrReturnDefault(final Object supposedJsonArray, + final T[] defaults, final Function constructor, T[] array) { final T[] result; final JSONArray windAnglesJsonArray; - if (windAnglesObject == null || !(windAnglesObject instanceof JSONArray) || (windAnglesJsonArray=(JSONArray) windAnglesObject).isEmpty()) { + if (supposedJsonArray == null || !(supposedJsonArray instanceof JSONArray) || (windAnglesJsonArray=(JSONArray) supposedJsonArray).isEmpty()) { result = defaults; } else { final List resultList = new ArrayList<>(); for (final Object number : windAnglesJsonArray) { resultList.add(constructor.apply((Double) number)); } - @SuppressWarnings("unchecked") - final T[] tArray = (T[]) resultList.toArray(); + final T[] tArray = resultList.toArray(array); result = tArray; } return result; diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index 0fdc6bd31a2..117477ff582 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -141,8 +141,8 @@ public class ORCCertificateJsonSerializer implements JsonSerializer iterator = importer.getCertificates().iterator(); assertTrue(iterator.hasNext()); ORCCertificate certificate = iterator.next(); - JSONObject serializedCertificate =serializer.serialize(certificate); + final Speed[] expectedTrueWindSpeeds = { new KnotSpeedImpl(6.1), new KnotSpeedImpl(8.2), new KnotSpeedImpl(10.6), new KnotSpeedImpl(12.1), new KnotSpeedImpl(14.9), new KnotSpeedImpl(16.7), new KnotSpeedImpl(20.1) }; + assertTrue(Arrays.equals(expectedTrueWindSpeeds, certificate.getTrueWindSpeeds())); + final Bearing[] expectedTrueWindAngles = { new DegreeBearingImpl(52.7), new DegreeBearingImpl(64.4), new DegreeBearingImpl(75), new DegreeBearingImpl(90), + new DegreeBearingImpl(110), new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) }; + assertTrue(Arrays.equals(expectedTrueWindAngles, certificate.getTrueWindAngles())); + JSONObject serializedCertificate = serializer.serialize(certificate); assertNotNull(serializedCertificate); ORCCertificate certificateAfterDeserialize = deserializer.deserialize(serializedCertificate); assertEquals(certificate.getId(), certificateAfterDeserialize.getId()); + assertTrue(Arrays.equals(expectedTrueWindSpeeds, certificateAfterDeserialize.getTrueWindSpeeds())); + assertTrue(Arrays.equals(expectedTrueWindAngles, certificateAfterDeserialize.getTrueWindAngles())); } @Test @@ -51,9 +64,13 @@ public class ORCCertificateJsonDeserializerTest { Iterator iterator = importer.getCertificates().iterator(); assertTrue(iterator.hasNext()); ORCCertificate certificate = iterator.next(); - JSONObject serializedCertificate =serializer.serialize(certificate); + assertTrue(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, certificate.getTrueWindSpeeds())); + assertTrue(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, certificate.getTrueWindAngles())); + JSONObject serializedCertificate = serializer.serialize(certificate); assertNotNull(serializedCertificate); ORCCertificate certificateAfterDeserialize = deserializer.deserialize(serializedCertificate); assertEquals(certificate.getId(), certificateAfterDeserialize.getId()); + assertTrue(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, certificateAfterDeserialize.getTrueWindSpeeds())); + assertTrue(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, certificateAfterDeserialize.getTrueWindAngles())); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java deleted file mode 100644 index 43eff18cfa4..00000000000 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/ORCCertificateJsonSerializerTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.sap.sailing.server.gateway.serialization.test; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Iterator; - -import org.json.simple.JSONObject; -import org.json.simple.parser.ParseException; -import org.junit.Before; -import org.junit.Test; - -import com.sap.sailing.domain.common.orc.ORCCertificate; -import com.sap.sailing.domain.orc.ORCCertificatesCollection; -import com.sap.sailing.domain.orc.ORCCertificatesImporter; -import com.sap.sailing.server.gateway.serialization.racelog.impl.ORCCertificateJsonSerializer; - -public class ORCCertificateJsonSerializerTest { - private ORCCertificateJsonSerializer serializer; - private static final String RESOURCES = "resources/rms/"; - @Before - public void setUp() { - serializer = new ORCCertificateJsonSerializer(); - } - - @Test - public void testOrcCertificateNewFormatSerializer() throws IOException, ParseException { - File fileGER = new File(RESOURCES + "newFormatCertificate.json"); - ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); - Iterator iterator = importer.getCertificates().iterator(); - assertTrue(iterator.hasNext()); - ORCCertificate certificate = iterator.next(); - JSONObject serializedCertificate =serializer.serialize(certificate); - assertNotNull(serializedCertificate); - } - @Test - public void testOrcCertificateOldFormatSerializer() throws IOException, ParseException { - File fileGER = new File(RESOURCES + "oldFormatCertificate.json"); - ORCCertificatesCollection importer = ORCCertificatesImporter.INSTANCE.read(new FileInputStream(fileGER)); - Iterator iterator = importer.getCertificates().iterator(); - assertTrue(iterator.hasNext()); - ORCCertificate certificate = iterator.next(); - JSONObject serializedCertificate =serializer.serialize(certificate); - assertNotNull(serializedCertificate); - } -} From 06a0dc79893a999e5678d8476423707ca22613b1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 09:49:30 +0100 Subject: [PATCH 56/85] formatting only --- .../racelog/impl/ORCCertificateJsonSerializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java index 117477ff582..08c3192d69d 100755 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/ORCCertificateJsonSerializer.java @@ -167,8 +167,8 @@ public class ORCCertificateJsonSerializer implements JsonSerializer Date: Tue, 11 Feb 2020 09:59:33 +0100 Subject: [PATCH 57/85] cleaned up ORCCertificateImpl_CustomFieldSerializer; using null for the default TWA/TWS arrays; fixed refactoring problem when Eclipse replaced ORCCertificateImpl by ORCCertificate erroneously --- ...CertificateImpl_CustomFieldSerializer.java | 49 +++---------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java index 33fb5e32efc..8a945d12b54 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java @@ -2,7 +2,6 @@ package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.orc.im import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import com.google.gwt.user.client.rpc.CustomFieldSerializer; @@ -25,7 +24,7 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali serialize(streamWriter, instance); } - public static void serialize(SerializationStreamWriter streamWriter, ORCCertificate instance) + public static void serialize(SerializationStreamWriter streamWriter, ORCCertificateImpl instance) throws SerializationException { streamWriter.writeString(instance.getId()); streamWriter.writeString(instance.getSailNumber()); @@ -46,8 +45,8 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali streamWriter.writeObject(new HashMap<>(instance.getLongDistanceSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getCircularRandomSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getNonSpinnakerSpeedPredictions())); - streamWriter.writeObject(Arrays.asList(instance.getTrueWindAngles())); - streamWriter.writeObject(Arrays.asList(instance.getTrueWindSpeeds())); + streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, instance.getTrueWindAngles()) ? null : instance.getTrueWindAngles()); + streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, instance.getTrueWindSpeeds()) ? null : instance.getTrueWindSpeeds()); } @Override @@ -93,10 +92,10 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali final Map circularRandomSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); @SuppressWarnings("unchecked") final Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); - - final Bearing[] trueWindAngles = getTrueWindAnglesFromStream(streamReader); - final Speed[] trueWindSpeeds = getTrueWindSpeedsFromStream(streamReader); - + final Bearing[] readTWAs = (Bearing[]) streamReader.readObject(); + final Bearing[] trueWindAngles = readTWAs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES : readTWAs; + final Speed[] readTWSs = (Speed[]) streamReader.readObject(); + final Speed[] trueWindSpeeds = readTWSs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS : readTWSs; return new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles, @@ -105,45 +104,13 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed); } - private static Speed[] getTrueWindSpeedsFromStream(SerializationStreamReader streamReader) { - Speed[] trueWindSpeeds = null; - try { - Object trueWindSpeedsObject = streamReader.readObject(); - if(trueWindSpeedsObject != null) { - @SuppressWarnings("unchecked") - List trueWIndSpeedList = (List) trueWindSpeedsObject; - trueWindSpeeds = new Speed[trueWIndSpeedList.size()]; - trueWIndSpeedList.toArray(trueWindSpeeds); - } - } catch (SerializationException e) { - trueWindSpeeds = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS; - } - return trueWindSpeeds; - } - - private static Bearing[] getTrueWindAnglesFromStream(SerializationStreamReader streamReader) { - Bearing[] trueWindAngles = null; - try { - Object trueWindAnglesObject = streamReader.readObject(); - if(trueWindAnglesObject != null) { - @SuppressWarnings("unchecked") - List trueWIndAnglesList = (List) trueWindAnglesObject; - trueWindAngles = new Bearing[trueWIndAnglesList.size()]; - trueWIndAnglesList.toArray(trueWindAngles); - } - } catch (SerializationException e) { - trueWindAngles = ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES; - } - return trueWindAngles; - } - @Override public void deserializeInstance(SerializationStreamReader streamReader, ORCCertificateImpl instance) throws SerializationException { deserialize(streamReader, instance); } - public static void deserialize(SerializationStreamReader streamReader, ORCCertificate instance) { + public static void deserialize(SerializationStreamReader streamReader, ORCCertificateImpl instance) { // Done by instantiateInstance } From 62432c49f5fe26c61b597a6ac3acde6d14ade300 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 10:46:58 +0100 Subject: [PATCH 58/85] fixed custom field serializer for ORCCertificateImpl --- ...RCCertificateImpl_CustomFieldSerializer.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java index 8a945d12b54..4de5a6bd7f7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/orc/impl/ORCCertificateImpl_CustomFieldSerializer.java @@ -2,6 +2,7 @@ package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.orc.im import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.google.gwt.user.client.rpc.CustomFieldSerializer; @@ -45,8 +46,10 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali streamWriter.writeObject(new HashMap<>(instance.getLongDistanceSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getCircularRandomSpeedPredictions())); streamWriter.writeObject(new HashMap<>(instance.getNonSpinnakerSpeedPredictions())); - streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, instance.getTrueWindAngles()) ? null : instance.getTrueWindAngles()); - streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, instance.getTrueWindSpeeds()) ? null : instance.getTrueWindSpeeds()); + streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, instance.getTrueWindAngles()) ? null : + Arrays.asList(instance.getTrueWindAngles())); + streamWriter.writeObject(Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, instance.getTrueWindSpeeds()) ? null : + Arrays.asList(instance.getTrueWindSpeeds())); } @Override @@ -92,10 +95,12 @@ public class ORCCertificateImpl_CustomFieldSerializer extends CustomFieldSeriali final Map circularRandomSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); @SuppressWarnings("unchecked") final Map nonSpinnakerSpeedPredictionsPerTrueWindSpeed = (Map) streamReader.readObject(); - final Bearing[] readTWAs = (Bearing[]) streamReader.readObject(); - final Bearing[] trueWindAngles = readTWAs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES : readTWAs; - final Speed[] readTWSs = (Speed[]) streamReader.readObject(); - final Speed[] trueWindSpeeds = readTWSs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS : readTWSs; + @SuppressWarnings("unchecked") + final List readTWAs = (List) streamReader.readObject(); + final Bearing[] trueWindAngles = readTWAs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES : readTWAs.toArray(new Bearing[readTWAs.size()]); + @SuppressWarnings("unchecked") + final List readTWSs = (List) streamReader.readObject(); + final Speed[] trueWindSpeeds = readTWSs == null ? ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS : readTWSs.toArray(new Speed[readTWSs.size()]); return new ORCCertificateImpl(trueWindSpeeds,trueWindAngles,idConsistingOfNatAuthCertNoAndBIN, sailnumber, boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles, From 233db33139ad896c49178abe2d65a22ab186fcf4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 11:18:46 +0100 Subject: [PATCH 59/85] cleaned up ORC JSON certificate parser --- .../impl/ORCCertificatesCollectionJSON.java | 95 +++++++++---------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java index 2636ef7c5e1..800fb6cd15c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCCertificatesCollectionJSON.java @@ -1,12 +1,14 @@ package com.sap.sailing.domain.orc.impl; import java.io.InputStream; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.bind.DatatypeConverter; @@ -61,6 +63,12 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec private static final String NON_SPINNAKER = "NS"; private static final String EMPTY_CERT_NO = " "; private static final String WIND_SPEEDS = "WindSpeeds"; + + /** + * Pattern to recognize a true wind angle in the allowances section. Group 1 contains + * the decimal number that represents the true wind angle in degrees. + */ + private static final Pattern twaPattern = Pattern.compile("R(\\d+(\\.\\d+)?)"); private Map certificateJsonObjectsByCertificateId; @@ -89,20 +97,15 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec Duration gph = null; Double cdl = null; TimePoint issueDate = null; - Map beatAngles = new HashMap<>(); - Map gybeAngles = new HashMap<>(); - Map> velocityPredictionPerTrueWindSpeedAndAngle = new HashMap<>(); - Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); // per - // nautical - // mile - Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per - // nautical - // mile - Map trueWindSpeedMap = new TreeMap();// Using TreeMap implementation to save the TrueWindSpeed - // So that it first sort the entries as well as no duplicates - Map trueWindAngleMap = new TreeMap();// Same like trueWindSpeedMap - Pattern p = Pattern.compile("(R\\d+(\\.\\d+)?)"); //Pattern to recognize the new pattern - JSONObject object = certificateJsonObjectsByCertificateId.get(certificateId); + final Map beatAngles = new HashMap<>(); + final Map gybeAngles = new HashMap<>(); + final Map> velocityPredictionPerTrueWindSpeedAndAngle = new HashMap<>(); + final Map> allowanceDurationsPerTrueWindAngleAndSpeed = new HashMap<>(); // per nautical mile + final Map> predefinedAllowanceDurationsPerTrueWindSpeed = new HashMap<>(); // per nautical mile + final Map trueWindSpeedMap = new TreeMap(); // Using TreeMap implementation to save the TrueWindSpeed + // so that it first sorts the entries as well as avoids duplicates + final Map trueWindAngleMap = new TreeMap(); // Same like trueWindSpeedMap + final JSONObject object = certificateJsonObjectsByCertificateId.get(certificateId); if (object == null) { // TODO Throw Exception for certificate by id not found. InvalidArgumentException? return null; @@ -134,12 +137,12 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec break; case ALLOWANCES: final JSONObject allowances = (JSONObject) object.get(ALLOWANCES); - // calculating the twa and tws before calculating the other dependent values in allowances object - Object windSpeedObject = allowances.get(WIND_SPEEDS); - if (windSpeedObject != null) { - final JSONArray array = (JSONArray) windSpeedObject; - for (int count = 0; count < array.size(); count++) { - Double twsValue = ((Number) array.get(count)).doubleValue(); + // calculating the TWA and TWS before calculating the other dependent values in allowances object + final Object windSpeedsObject = allowances.get(WIND_SPEEDS); + if (windSpeedsObject != null) { + final JSONArray twsArray = (JSONArray) windSpeedsObject; + for (final Object twsObject : twsArray) { + Double twsValue = ((Number) twsObject).doubleValue(); trueWindSpeedMap.put(twsValue, new KnotSpeedImpl(twsValue)); } } else { @@ -151,24 +154,20 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec trueWindSpeedMap.keySet().toArray(trueWindSpeedArray); for (final Object aKey : allowances.keySet()) { String keyString = (String) aKey; - boolean found = p.matcher(keyString).lookingAt(); - if (found) { + final Matcher matcher = twaPattern.matcher(keyString); + if (matcher.lookingAt()) { final JSONArray array = (JSONArray) allowances.get(keyString); - Map twsMap = new HashMap<>(); + final Map twsMap = new HashMap<>(); for (int i = 0; i < array.size(); i++) { - Double allowanceValue = ((Number) array.get(i)).doubleValue(); - twsMap.put(trueWindSpeedMap.get(trueWindSpeedArray[i]), - new SecondsDurationImpl(allowanceValue)); - + final Double allowanceValue = ((Number) array.get(i)).doubleValue(); + twsMap.put(trueWindSpeedMap.get(trueWindSpeedArray[i]), new SecondsDurationImpl(allowanceValue)); } - Double trueWindAngleValue = Double.parseDouble((keyString).substring(1)); + final Double trueWindAngleValue = Double.parseDouble(matcher.group(1)); trueWindAngleMap.put(trueWindAngleValue, new DegreeBearingImpl(trueWindAngleValue)); - allowanceDurationsPerTrueWindAngleAndSpeed.put(trueWindAngleMap.get(trueWindAngleValue), - twsMap); + allowanceDurationsPerTrueWindAngleAndSpeed.put(trueWindAngleMap.get(trueWindAngleValue), twsMap); } } - - + // Now read the various allowances, assuming we find values for the TWS "bins" identified above for (final Object aKey : allowances.keySet()) { final JSONArray array = (JSONArray) allowances.get(aKey); if (((String) aKey).equals(BEAT_ANGLE)) { @@ -187,7 +186,7 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec } Map twsMap = new HashMap<>(); //ignore true wind angles key - if (!p.matcher((String) aKey).lookingAt()) { + if (!twaPattern.matcher((String) aKey).lookingAt()) { for (int i = 0; i < array.size(); i++) { twsMap.put(new KnotSpeedImpl(trueWindSpeedArray[i]), new SecondsDurationImpl(((Number) array.get(i)).doubleValue())); @@ -208,13 +207,9 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec break; } } - - trueWindSpeedMap.keySet().forEach(tws -> { - velocityPredictionPerTrueWindSpeedAndAngle.put(trueWindSpeedMap.get(tws), new HashMap<>()); - }); - - for (Bearing keyTWA : allowanceDurationsPerTrueWindAngleAndSpeed.keySet()) { - for (Speed keyTWS : allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).keySet()) { + trueWindSpeedMap.values().forEach(tws -> velocityPredictionPerTrueWindSpeedAndAngle.put(tws, new HashMap<>())); + for (final Bearing keyTWA : allowanceDurationsPerTrueWindAngleAndSpeed.keySet()) { + for (final Speed keyTWS : allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).keySet()) { velocityPredictionPerTrueWindSpeedAndAngle.get(keyTWS).put(keyTWA, ORCCertificateImpl.NAUTICAL_MILE .inTime(allowanceDurationsPerTrueWindAngleAndSpeed.get(keyTWA).get(keyTWS))); } @@ -243,22 +238,24 @@ public class ORCCertificatesCollectionJSON extends AbstractORCCertificatesCollec nonSpinnakerSpeedPredictionPerTrueWindSpeed.put(tws, ORCCertificateImpl.NAUTICAL_MILE .inTime(predefinedAllowanceDurationsPerTrueWindSpeed.get(NON_SPINNAKER).get(tws))); } - Bearing[] dynamicAllowancesTrueWindAngles = new Bearing[trueWindAngleMap.values().size()]; - Speed[] dynamicAllowancesTrueWindSpeeds = new Speed[trueWindSpeedMap.values().size()]; - trueWindAngleMap.values().toArray(dynamicAllowancesTrueWindAngles); - trueWindSpeedMap.values().toArray(dynamicAllowancesTrueWindSpeeds); - - return new ORCCertificateImpl(dynamicAllowancesTrueWindSpeeds, dynamicAllowancesTrueWindAngles, getId(object), - sailNumber, boatName, boatclass, length, gph, cdl, issueDate, + final Bearing[] dynamicAllowancesTrueWindAngles = trueWindAngleMap.values().toArray(new Bearing[trueWindAngleMap.values().size()]); + final Speed[] dynamicAllowancesTrueWindSpeeds = trueWindSpeedMap.values().toArray(new Speed[trueWindSpeedMap.values().size()]); + // replace array by the default arrays if they match; this will save the space for the dynamically-parsed arrays + return new ORCCertificateImpl( + Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS, dynamicAllowancesTrueWindSpeeds) + ? ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS + : dynamicAllowancesTrueWindSpeeds, + Arrays.equals(ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES, dynamicAllowancesTrueWindAngles) + ? ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES + : dynamicAllowancesTrueWindAngles, + getId(object), sailNumber, boatName, boatclass, length, gph, cdl, issueDate, velocityPredictionPerTrueWindSpeedAndAngle, beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, gybeAngles, runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed, windwardLeewardSpeedPredictionPerTrueWindSpeed, longDistanceSpeedPredictionPerTrueWindSpeed, circularRandomSpeedPredictionPerTrueWindSpeed, nonSpinnakerSpeedPredictionPerTrueWindSpeed); - } - private String getId(JSONObject certificateAsJson) { return getIdFromFields(certificateAsJson.get(NAT_AUTH).toString(), certificateAsJson.get(CERT_NO) == null ? EMPTY_CERT_NO : certificateAsJson.get(CERT_NO).toString(), From 2f790825f6c9733e726f2a3a999d9625b1ad9964 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 11:46:31 +0100 Subject: [PATCH 60/85] added a failing test case for performance curve calculation with non-default bins --- .../resources/orc/newFormatCertificate.json | 221 ++++++++++++++++++ .../domain/orc/TestORCPerformanceCurve.java | 10 +- 2 files changed, 228 insertions(+), 3 deletions(-) create mode 100755 java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json diff --git a/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json b/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json new file mode 100755 index 00000000000..f9c584edab4 --- /dev/null +++ b/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json @@ -0,0 +1,221 @@ +{ + "rms":[ + { + "NatAuth":"GR", + "BIN":"GR317", + "CertNo":"A00073", + "RefNo":"N\/A", + "SailNo":"GRE-317", + "YachtName":"AVANTES", + "Class":"FIRST 31.7", + "Builder":"BENETEAU", + "Designer":"FINOT", + "Address3":"I.O. HALKIDAS", + "C_Type":"INTL", + "Family":"ORC", + "Division":"C", + "IssueDate":"2020-02-05T12:54:23.484Z", + "Age_Year":1998, + "CrewWT":550, + "LOA":9.49, + "IMSL":9.003, + "Draft":1.94, + "MB":3.216, + "Dspl_Measurement":3936, + "Stability_Index":103.7, + "Dynamic_Allowance":0.31, + "GPH":678, + "TMF_Inshore":0.9229, + "ILCWA":731.4, + "TMF_Offshore":0.9145, + "OSN":656.1, + "CDL":8.155, + "TN_Offshore_Low":0.8578, + "TN_Offshore_Medium":1.1292, + "TN_Offshore_High":1.2751, + "TN_Inshore_Low":0.6639, + "TN_Inshore_Medium":0.9158, + "TN_Inshore_High":1.0666, + "TND_Offshore_Low":786.9, + "TND_Offshore_Medium":597.8, + "TND_Offshore_High":529.4, + "TND_Inshore_Low":1016.7, + "TND_Inshore_Medium":737.1, + "TND_Inshore_High":632.9, + "OSN_Jibs":688.6, + "TMF_Jibs":0.8713, + "Dspl_Sailing":4660, + "WSS":22.41, + "Area_Main":27.04, + "Area_Jib":22.08, + "Area_Sym":61.48, + "Area_ASym":0, + "GPH_Jibs":714.6, + "GPH_Jibs_TOT":0.8396, + "Allowances":{ + "WindSpeeds":[ + 6.1, + 8.2, + 10.6, + 12.1, + 14.9, + 16.7, + 20.1 + ], + "Beat":[ + 1175.6, + 968.7, + 854, + 780.9, + 752, + 744.6, + 744.9 + ], + "Run":[ + 1074.7, + 847.7, + 716.4, + 628.3, + 566, + 526.9, + 480.1 + ], + "BeatAngle":[ + 43.5, + 41.6, + 40.5, + 39.8, + 39, + 38.6, + 39 + ], + "GybeAngle":[ + 146, + 150, + 153, + 158, + 178, + 180, + 180 + ], + "WL":[ + 1125.2, + 908.2, + 785.2, + 704.6, + 659, + 635.8, + 612.5 + ], + "CR":[ + 942.4, + 757.3, + 657.2, + 598.8, + 563, + 540.1, + 512.7 + ], + "OC":[ + 1120.1, + 852, + 706, + 616.2, + 571.7, + 539.8, + 487.7 + ], + "NS":[ + 1008.9, + 804, + 691.6, + 625.1, + 584.2, + 558, + 527.3 + ], + "OL":[ + 1077.4, + 874.1, + 761.4, + 694.6, + 658.2, + 638.7, + 615.2 + ], + "R52.7":[ + 767, + 645, + 577.3, + 544, + 530.1, + 525.8, + 525 + ], + "R64.4":[ + 725.4, + 613.2, + 556.7, + 530.3, + 516.8, + 510.9, + 508.9 + ], + "R75":[ + 697.1, + 589.3, + 541.9, + 519, + 502.9, + 490.9, + 480.5 + ], + "R90":[ + 694.3, + 577.7, + 526.9, + 505.5, + 495.9, + 481.7, + 454.3 + ], + "R110":[ + 688.6, + 568.4, + 520.4, + 493.9, + 469.8, + 450.8, + 430.3 + ], + "R120":[ + 710.9, + 583.7, + 526.8, + 498.8, + 473.3, + 448.4, + 409.9 + ], + "R135":[ + 788.3, + 640.5, + 556.1, + 517.8, + 492.5, + 466.8, + 416.9 + ], + "R150":[ + 930.8, + 734.2, + 621.9, + 551.9, + 517.7, + 494, + 446.5 + ] + } + } + ] +} diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index 94bce72f709..372b756c7ed 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -49,6 +49,7 @@ public class TestORCPerformanceCurve { private static ORCPerformanceCurveCourse alturaCourse; private static ORCCertificatesCollection importerLocal; + private static ORCCertificatesCollection importerWithSpecificBins; private static ORCCertificatesCollection importerOnline; private static final String RESOURCES = "resources/orc/"; @@ -76,11 +77,12 @@ public class TestORCPerformanceCurve { legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.03), new DegreeBearingImpl(165))); legs.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1.17), new DegreeBearingImpl(180))); alturaCourse = new ORCPerformanceCurveCourseImpl(legs); //this course is the same course as seen in the Altura "IMS Explanation" sheet - // Local File: File fileGER = new File(RESOURCES + "GER2019.json"); importerLocal = new ORCCertificatesJsonImporter().read(new FileInputStream(fileGER)); - + // Local File new format with specific TWA/TWS bins: + File fileWithSpecificBins = new File(RESOURCES + "newFormatCertificate.json"); + importerWithSpecificBins = new ORCCertificatesJsonImporter().read(new FileInputStream(fileWithSpecificBins)); // Online File: importerOnline = new ORCCertificatesJsonImporter().read(new URL("https://data.orc.org/public/WPub.dll?action=DownRMS&CountryId=GER&ext=json").openStream()); } @@ -298,12 +300,14 @@ public class TestORCPerformanceCurve { ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555"); ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300"); ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564"); + ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("GRA00073GR317"); ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, complexCourse); ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, complexCourse); ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, complexCourse); ORCPerformanceCurve performanceCurveBank = new ORCPerformanceCurveImpl(certificateBank, complexCourse); ORCPerformanceCurve performanceCurveHaspa = new ORCPerformanceCurveImpl(certificateHaspa, complexCourse); ORCPerformanceCurve performanceCurveHalbtrocken = new ORCPerformanceCurveImpl(certificateHalbtrocken, complexCourse); + ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, complexCourse); assertEquals(Duration.ONE_HOUR.times(1.0).asHours(), performanceCurveMilan.getAllowancePerCourse(new KnotSpeedImpl(15.75777)).asHours(), accuracy); assertEquals(Duration.ONE_HOUR.times(1.25).asHours(), performanceCurveBank.getAllowancePerCourse(new KnotSpeedImpl(15.27808)).asHours(), accuracy); @@ -311,7 +315,7 @@ public class TestORCPerformanceCurve { assertEquals(Duration.ONE_HOUR.times(1.25).asHours(), performanceCurveHaspa.getAllowancePerCourse(new KnotSpeedImpl(14.44527)).asHours(), accuracy); assertEquals(Duration.ONE_HOUR.times(1.5).asHours(), performanceCurveTutima.getAllowancePerCourse(new KnotSpeedImpl(10.86927)).asHours(), accuracy); assertEquals(Duration.ONE_HOUR.times(2.0).asHours(), performanceCurveHalbtrocken.getAllowancePerCourse(new KnotSpeedImpl(9.13385)).asHours(), accuracy); - + assertEquals(Duration.ONE_HOUR.times(2.0).asHours(), performanceCurveSpecificBins.getAllowancePerCourse(new KnotSpeedImpl(9.13385)).asHours(), accuracy); } } From 2e6365be6604ec844adc5f26d258cd76a3aa3d4c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 11:50:17 +0100 Subject: [PATCH 61/85] fixed performance curve calculation for specific non-default bins --- .../com/sap/sailing/domain/orc/TestORCPerformanceCurve.java | 5 ++++- .../sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index 372b756c7ed..0d7e72adeeb 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -268,18 +268,21 @@ public class TestORCPerformanceCurve { ORCCertificate certificateBank = importerLocal.getCertificateById("GER140755GER5555"); ORCCertificate certificateHaspa = importerLocal.getCertificateById("GER141411GER6300"); ORCCertificate certificateHalbtrocken = importerLocal.getCertificateById("GER141432GER5564"); + ORCCertificate certificateWithSpecificBins=importerWithSpecificBins.getCertificateById("GRA00073GR317"); ORCPerformanceCurve performanceCurveMoana = new ORCPerformanceCurveImpl(certificateMoana, complexCourse); ORCPerformanceCurve performanceCurveMilan = new ORCPerformanceCurveImpl(certificateMilan, complexCourse); ORCPerformanceCurve performanceCurveTutima = new ORCPerformanceCurveImpl(certificateTutima, complexCourse); ORCPerformanceCurve performanceCurveBank = new ORCPerformanceCurveImpl(certificateBank, complexCourse); ORCPerformanceCurve performanceCurveHaspa = new ORCPerformanceCurveImpl(certificateHaspa, complexCourse); ORCPerformanceCurve performanceCurveHalbtrocken = new ORCPerformanceCurveImpl(certificateHalbtrocken, complexCourse); + ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, complexCourse); assertEquals(15.75777 , performanceCurveMilan .getImpliedWind(Duration.ONE_HOUR.times(1.0)).getKnots(), accuracy); assertEquals(15.27808 , performanceCurveBank .getImpliedWind(Duration.ONE_HOUR.times(1.25)).getKnots(), accuracy); assertEquals(15.10141 , performanceCurveMoana .getImpliedWind(Duration.ONE_HOUR.times(1.25)).getKnots(), accuracy); assertEquals(14.44527 , performanceCurveHaspa .getImpliedWind(Duration.ONE_HOUR.times(1.25)).getKnots(), accuracy); assertEquals(10.86927 , performanceCurveTutima .getImpliedWind(Duration.ONE_HOUR.times(1.5)).getKnots(), accuracy); assertEquals(9.13385 , performanceCurveHalbtrocken.getImpliedWind(Duration.ONE_HOUR.times(2.0)).getKnots(), accuracy); + assertEquals(10.83989 , performanceCurveSpecificBins.getImpliedWind(Duration.ONE_HOUR.times(2.0)).getKnots(), accuracy); } @Test @@ -315,7 +318,7 @@ public class TestORCPerformanceCurve { assertEquals(Duration.ONE_HOUR.times(1.25).asHours(), performanceCurveHaspa.getAllowancePerCourse(new KnotSpeedImpl(14.44527)).asHours(), accuracy); assertEquals(Duration.ONE_HOUR.times(1.5).asHours(), performanceCurveTutima.getAllowancePerCourse(new KnotSpeedImpl(10.86927)).asHours(), accuracy); assertEquals(Duration.ONE_HOUR.times(2.0).asHours(), performanceCurveHalbtrocken.getAllowancePerCourse(new KnotSpeedImpl(9.13385)).asHours(), accuracy); - assertEquals(Duration.ONE_HOUR.times(2.0).asHours(), performanceCurveSpecificBins.getAllowancePerCourse(new KnotSpeedImpl(9.13385)).asHours(), accuracy); + assertEquals(Duration.ONE_HOUR.times(2.0).asHours(), performanceCurveSpecificBins.getAllowancePerCourse(new KnotSpeedImpl(10.83989)).asHours(), accuracy); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java index 05f52de9e46..bc413ce7d16 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java @@ -142,7 +142,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv for (final ORCPerformanceCurveLeg leg : course.getLegs()) { allowancesPerLeg.put(leg, createAllowancePerLeg(leg, certificate)); } - for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { + for (final Speed tws : certificate.getTrueWindSpeeds()) { Duration allowancePerTws = new SecondsDurationImpl(0); for (final ORCPerformanceCurveLeg leg : course.getLegs()) { allowancePerTws = allowancePerTws.plus(allowancesPerLeg.get(leg).get(tws)); From a702585bde1933208a8fd2b998239cb378ce9742 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 12:10:35 +0100 Subject: [PATCH 62/85] added failing test case showing that specific bin implied wind calculation is broken --- .../domain/orc/TestORCPerformanceCurve.java | 23 +++++++++++++++++++ .../orc/impl/ORCPerformanceCurveImpl.java | 8 +++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index 0d7e72adeeb..a466378199d 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -33,7 +33,10 @@ import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveCourseImpl; import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveLegImpl; import com.sap.sailing.domain.orc.impl.ORCCertificatesJsonImporter; import com.sap.sailing.domain.orc.impl.ORCPerformanceCurveImpl; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; +import com.sap.sse.common.Speed; import com.sap.sse.common.Util; import com.sap.sse.common.impl.DegreeBearingImpl; @@ -249,6 +252,26 @@ public class TestORCPerformanceCurve { } } + @Test + public void testAllowancesAndImpliedWindForSpecificBins() throws FunctionEvaluationException { + final Distance ONE_NAUTICAL_MILE = new NauticalMileDistance(1.0); + final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("GRA00073GR317"); + for (final Bearing twa : certificateWithSpecificBins.getTrueWindAngles()) { + final ORCPerformanceCurveCourse singleLegOneMileCourseWithTwa = createSingleLegCourseWithTwa(twa); + final ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, singleLegOneMileCourseWithTwa); + for (final Speed tws : certificateWithSpecificBins.getTrueWindSpeeds()) { + Assert.assertEquals(certificateWithSpecificBins.getVelocityPredictionPerTrueWindSpeedAndAngle().get(tws).get(twa).getDuration(ONE_NAUTICAL_MILE), + performanceCurveSpecificBins.getAllowancePerCourse(tws)); + } + } + } + + private ORCPerformanceCurveCourse createSingleLegCourseWithTwa(Bearing twa) { + List list = new ArrayList<>(); + list.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1), twa)); + return new ORCPerformanceCurveCourseImpl(list); + } + // Tests for the calculations with a more complex course which contains some special leg types. (circular random or other) @Test public void testComplexCourseImpliedWind() throws FunctionEvaluationException, MaxIterationsExceededException { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java index bc413ce7d16..5b17b3d1458 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java @@ -294,9 +294,9 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv Map beatAngles, Map beatVMGPredictionPerTrueWindSpeed, Map runAngles, Map runVMGPredictionPerTrueWindSpeed, Speed trueWindSpeed, Bearing trueWindAngle) throws FunctionEvaluationException, IllegalArgumentException { - - return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances,beatAngles,beatVMGPredictionPerTrueWindSpeed, - runAngles,runVMGPredictionPerTrueWindSpeed,trueWindSpeed,trueWindAngle, ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES); + return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances, beatAngles, + beatVMGPredictionPerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, trueWindSpeed, + trueWindAngle, ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES); } public Speed getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(Map> twaAllowances, @@ -339,7 +339,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv * the {@link #course} (which may be a prefix of a longer course) at the true wind speed provided to the resulting * map as the key. Iteration order is in ascending true wind speeds. */ - public LinkedHashMap getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException { + private LinkedHashMap getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException { final LinkedHashMap result = new LinkedHashMap<>(); for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { result.put(tws, getAllowancePerCourse(tws)); From 341abf655f10f2581f6cdafd83d901d479963421 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Tue, 11 Feb 2020 13:04:30 +0100 Subject: [PATCH 63/85] Bug 5195: Several adjustments to ensure consistent usage of RoleDefinitions for RolePrototypes in the security model to prevent incompatible usages of objects that effectively represent the same RoleDefinition. --- .../sse/security/shared/RolePrototype.java | 28 ++------------ .../sse/security/interfaces/UserStore.java | 4 ++ .../security/test/PermissionCheckerTest.java | 9 +++-- .../sse/security/test/RoleIdentifierTest.java | 6 ++- .../userstore/mongodb/UserStoreImpl.java | 38 ++++++++++++------- .../security/impl/SecurityServiceImpl.java | 7 ++-- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/RolePrototype.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/RolePrototype.java index ead2f5c38c5..8d11b041e35 100755 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/RolePrototype.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/RolePrototype.java @@ -4,12 +4,13 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; +import com.sap.sse.common.NamedWithID; import com.sap.sse.common.Util; -import com.sap.sse.security.shared.impl.SecuredSecurityTypes; -public abstract class RolePrototype implements RoleDefinition { - private static final long serialVersionUID = 3291793984984443193L; +public abstract class RolePrototype implements NamedWithID { + private static final long serialVersionUID = -3911998376131317304L; + private final UUID id; private final String name; private final Set permissions; @@ -46,17 +47,14 @@ public abstract class RolePrototype implements RoleDefinition { return id; } - @Override public Set getPermissions() { return permissions; } - @Override public void setName(String newName) { throw new UnsupportedOperationException("Cannot change the name of role " + getName()); } - @Override public void setPermissions(Iterable permissions) { throw new UnsupportedOperationException("Cannot change the permissions of role " + getName()); } @@ -85,22 +83,4 @@ public abstract class RolePrototype implements RoleDefinition { return false; return true; } - - @Override - public HasPermissions getPermissionType() { - return SecuredSecurityTypes.ROLE_DEFINITION; - } - - @Override - public QualifiedObjectIdentifier getIdentifier() { - return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier()); - } - - public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() { - return getTypeRelativeObjectIdentifier(this); - } - - public static TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier(RoleDefinition roleDefinition) { - return new TypeRelativeObjectIdentifier(roleDefinition.getIdAsString()); - } } diff --git a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java index ae6246ec2b7..8f0142c9610 100644 --- a/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java +++ b/java/com.sap.sse.security.interface/src/com/sap/sse/security/interfaces/UserStore.java @@ -1,6 +1,8 @@ package com.sap.sse.security.interfaces; import com.sap.sse.security.shared.BasicUserStore; +import com.sap.sse.security.shared.RoleDefinition; +import com.sap.sse.security.shared.RolePrototype; import com.sap.sse.security.shared.UserGroupManagementException; import com.sap.sse.security.shared.UserManagementException; import com.sap.sse.security.shared.impl.Role; @@ -104,4 +106,6 @@ public interface UserStore extends BasicUserStore { void removeAllQualifiedRolesForUser(User user); void removeAllQualifiedRolesForUserGroup(UserGroup userGroup); + + RoleDefinition getRoleDefinitionByPrototype(RolePrototype rolePrototype); } diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java index 1743e7a9ad4..1c97e57990c 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/PermissionCheckerTest.java @@ -109,7 +109,8 @@ public class PermissionCheckerTest { assertFalse(PermissionChecker.isPermitted(eventReadPermission, user, tenants, null, null, ownership, acl)); userStore.addRoleForUser(user.getName(), - new Role(AdminRole.getInstance(), /* qualified for userTenant */ null, /* qualified for user */ user)); + new Role(userStore.getRoleDefinitionByPrototype(AdminRole.getInstance()), + /* qualified for userTenant */ null, /* qualified for user */ user)); // having the admin role qualified for objects owned by user should help assertTrue(PermissionChecker.isPermitted(eventReadPermission, user, tenants, null, null, ownership, acl)); @@ -145,7 +146,8 @@ public class PermissionCheckerTest { /* tenantOwner */ null, regattaName); // grant user the admin role, but only for objects owned by the user (leaderboard, but not regatta) userStore.addRoleForUser(user.getName(), - new Role(AdminRole.getInstance(), /* qualifiedForTenant */ null, /* qualifiedForUser */ user)); + new Role(userStore.getRoleDefinitionByPrototype(AdminRole.getInstance()), /* qualifiedForTenant */ null, + /* qualifiedForUser */ user)); assertTrue(realm.isPermitted(principalCollection, leaderboardPermission.toString())); assertFalse(realm.isPermitted(principalCollection, regattaPermission.toString())); accessControlStore.setOwnership(SecuredDomainType.REGATTA.getQualifiedObjectIdentifier(regattaIdentifier), /* userOwner */ null, @@ -155,7 +157,8 @@ public class PermissionCheckerTest { assertFalse(realm.isPermitted(principalCollection, regattaPermission.toString())); // but now we assign the admin role to the user, qualified for objects owned by the group owner: userStore.addRoleForUser(user.getName(), - new Role(AdminRole.getInstance(), /* qualifiedForTenant */ userTenant, /* qualifiedForUser */ null)); + new Role(userStore.getRoleDefinitionByPrototype(AdminRole.getInstance()), + /* qualifiedForTenant */ userTenant, /* qualifiedForUser */ null)); assertTrue(realm.isPermitted(principalCollection, leaderboardPermission.toString())); // now the user should be granted permission because admin gets *, and the user gets admin on all objects owned by userTenant assertTrue(realm.isPermitted(principalCollection, regattaPermission.toString())); diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/RoleIdentifierTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/RoleIdentifierTest.java index 216b2eaad8c..65a1ef8c343 100755 --- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/RoleIdentifierTest.java +++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/RoleIdentifierTest.java @@ -2,6 +2,7 @@ package com.sap.sse.security.test; import static org.junit.Assert.assertEquals; +import java.util.Collections; import java.util.UUID; import org.junit.Test; @@ -10,7 +11,6 @@ import com.sap.sse.security.shared.HasPermissions.DefaultActions; import com.sap.sse.security.shared.QualifiedObjectIdentifier; import com.sap.sse.security.shared.RoleDefinition; import com.sap.sse.security.shared.RoleDefinitionImpl; -import com.sap.sse.security.shared.RolePrototype; import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; /** @@ -39,8 +39,10 @@ public class RoleIdentifierTest { @Test public void testRolePrototypeIdentifier() { - final RoleDefinition roleDefinition = new RolePrototype("Test Role", UUID.randomUUID().toString()) { + final RoleDefinition roleDefinition = new RoleDefinitionImpl(UUID.randomUUID(), "Test Role", + Collections.emptySet()) { private static final long serialVersionUID = 6504908715414284115L; + @Override public String getIdAsString() { return "abc'::\\\\://|\\?/\\///\\"; diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java index 0c5eaf7d96e..ed5b30f6e3e 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java @@ -28,6 +28,7 @@ import com.sap.sse.security.shared.AdminRole; import com.sap.sse.security.shared.PredefinedRoles; import com.sap.sse.security.shared.RoleDefinition; import com.sap.sse.security.shared.RoleDefinitionImpl; +import com.sap.sse.security.shared.RolePrototype; import com.sap.sse.security.shared.SecurityUser; import com.sap.sse.security.shared.UserGroupManagementException; import com.sap.sse.security.shared.UserManagementException; @@ -266,20 +267,8 @@ public class UserStoreImpl implements UserStore { @Override public void ensureDefaultRolesExist() { - final AdminRole adminRolePrototype = AdminRole.getInstance(); - if (getRoleDefinition(adminRolePrototype.getId()) == null) { - logger.info("No admin role found. Creating default role \"" + adminRolePrototype.getName() - + "\" with permission \"" + AdminRole.getInstance().getPermissions() + "\""); - createRoleDefinition((UUID) adminRolePrototype.getId(), adminRolePrototype.getName(), - adminRolePrototype.getPermissions()); - } - final UserRole userRolePrototype = UserRole.getInstance(); - if (getRoleDefinition(userRolePrototype.getId()) == null) { - logger.info("No user role found. Creating default role \"" + userRolePrototype.getName() - + "\" with permission \"" + userRolePrototype.getPermissions() + "\""); - createRoleDefinition((UUID) userRolePrototype.getId(), userRolePrototype.getName(), - userRolePrototype.getPermissions()); - } + getOrCreateRoleDefinitionByPrototype(AdminRole.getInstance()); + getOrCreateRoleDefinitionByPrototype(UserRole.getInstance()); for (final PredefinedRoles otherPredefinedRole : PredefinedRoles.values()) { if (getRoleDefinition(otherPredefinedRole.getId()) == null) { logger.info("Predefined role definition " + otherPredefinedRole + " not found; creating"); @@ -291,6 +280,27 @@ public class UserStoreImpl implements UserStore { } } } + + private RoleDefinition getOrCreateRoleDefinitionByPrototype(RolePrototype rolePrototype) { + RoleDefinition roleDefinition = getRoleDefinition(rolePrototype.getId()); + if (roleDefinition == null) { + logger.info("No " + rolePrototype.getName() + " role found. Creating default role \"" + + rolePrototype.getName() + "\" with permission \"" + rolePrototype.getPermissions() + "\""); + roleDefinition = createRoleDefinition(rolePrototype.getId(), rolePrototype.getName(), rolePrototype.getPermissions()); + } + return roleDefinition; + } + + public RoleDefinition getRoleDefinitionByPrototype(RolePrototype rolePrototype) { + final RoleDefinition roleDefinition = getRoleDefinition(rolePrototype.getId()); + if (roleDefinition == null) { + final String errorMsg = "No " + rolePrototype.getName() + " role definition found by ID " + + rolePrototype.getId() + "." + "RoleDefinitions for prototypes are required to exist on usage."; + logger.severe(errorMsg); + throw new IllegalStateException(errorMsg); + } + return roleDefinition; + } @Override public String getServerGroupName() { 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 e83aae93808..81033ca5f3a 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 @@ -863,12 +863,13 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat } private void addUserRoleToUser(final User user) { - addRoleForUserAndSetUserAsOwner(user, - new Role(UserRole.getInstance(), /* tenant qualifier */ null, /* user qualifier */ user)); + addRoleForUserAndSetUserAsOwner(user, new Role(store.getRoleDefinitionByPrototype(UserRole.getInstance()), + /* tenant qualifier */ null, /* user qualifier */ user)); } private void addUserRoleForGroupToUser(final UserGroup group, final User user) { - addRoleForUserAndSetUserAsOwner(user, new Role(UserRole.getInstance(), /* tenant qualifier */ group, /* user qualifier */ null)); + addRoleForUserAndSetUserAsOwner(user, new Role(store.getRoleDefinitionByPrototype(UserRole.getInstance()), + /* tenant qualifier */ group, /* user qualifier */ null)); } private UserGroup getOrCreateTenantForUser(User user) throws UserGroupManagementException { From 683ab5a51f369c6f2616ff7b0b71d967fb874657 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Tue, 11 Feb 2020 15:17:14 +0100 Subject: [PATCH 64/85] Bug 5195: Removed unused CustomFieldSerializers for RolePrototypes --- .../AdminRole_CustomFieldSerializer.java | 45 ------------------ .../UserRole_CustomFieldSerializer.java | 46 ------------------- .../AdminRole_CustomFieldSerializer.java | 45 ------------------ .../UserRole_CustomFieldSerializer.java | 46 ------------------- 4 files changed, 182 deletions(-) delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java delete mode 100755 java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java delete mode 100755 java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java deleted file mode 100755 index 8f1abcbc3ee..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.google.gwt.user.client.rpc.core.com.sap.sse.security.shared; - -import com.google.gwt.user.client.rpc.CustomFieldSerializer; -import com.google.gwt.user.client.rpc.SerializationException; -import com.google.gwt.user.client.rpc.SerializationStreamReader; -import com.google.gwt.user.client.rpc.SerializationStreamWriter; -import com.sap.sse.security.shared.AdminRole; - -public class AdminRole_CustomFieldSerializer extends CustomFieldSerializer { - - @Override - public void serializeInstance(SerializationStreamWriter streamWriter, AdminRole instance) - throws SerializationException { - serialize(streamWriter, instance); - } - - public static void serialize(SerializationStreamWriter streamWriter, AdminRole instance) - throws SerializationException { - } - - @Override - public boolean hasCustomInstantiateInstance() { - return true; - } - - @Override - public AdminRole instantiateInstance(SerializationStreamReader streamReader) throws SerializationException { - return instantiate(streamReader); - } - - public static AdminRole instantiate(SerializationStreamReader streamReader) throws SerializationException { - return AdminRole.getInstance(); - } - - @Override - public void deserializeInstance(SerializationStreamReader streamReader, AdminRole instance) - throws SerializationException { - deserialize(streamReader, instance); - } - - public static void deserialize(SerializationStreamReader streamReader, AdminRole instance) { - // Done by instantiateInstance - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java deleted file mode 100755 index 96625e142f0..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.google.gwt.user.client.rpc.core.com.sap.sse.security.shared; - -import com.google.gwt.user.client.rpc.CustomFieldSerializer; -import com.google.gwt.user.client.rpc.SerializationException; -import com.google.gwt.user.client.rpc.SerializationStreamReader; -import com.google.gwt.user.client.rpc.SerializationStreamWriter; -import com.sap.sse.security.shared.RolePrototype; -import com.sap.sse.security.shared.UserRole; - -public class UserRole_CustomFieldSerializer extends CustomFieldSerializer { - - @Override - public void serializeInstance(SerializationStreamWriter streamWriter, UserRole instance) - throws SerializationException { - serialize(streamWriter, instance); - } - - public static void serialize(SerializationStreamWriter streamWriter, UserRole instance) - throws SerializationException { - } - - @Override - public boolean hasCustomInstantiateInstance() { - return true; - } - - @Override - public UserRole instantiateInstance(SerializationStreamReader streamReader) throws SerializationException { - return instantiate(streamReader); - } - - public static UserRole instantiate(SerializationStreamReader streamReader) throws SerializationException { - return UserRole.getInstance(); - } - - @Override - public void deserializeInstance(SerializationStreamReader streamReader, UserRole instance) - throws SerializationException { - deserialize(streamReader, instance); - } - - public static void deserialize(SerializationStreamReader streamReader, RolePrototype instance) { - // Done by instantiateInstance - } - -} diff --git a/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java b/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java deleted file mode 100755 index 8f1abcbc3ee..00000000000 --- a/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/AdminRole_CustomFieldSerializer.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.google.gwt.user.client.rpc.core.com.sap.sse.security.shared; - -import com.google.gwt.user.client.rpc.CustomFieldSerializer; -import com.google.gwt.user.client.rpc.SerializationException; -import com.google.gwt.user.client.rpc.SerializationStreamReader; -import com.google.gwt.user.client.rpc.SerializationStreamWriter; -import com.sap.sse.security.shared.AdminRole; - -public class AdminRole_CustomFieldSerializer extends CustomFieldSerializer { - - @Override - public void serializeInstance(SerializationStreamWriter streamWriter, AdminRole instance) - throws SerializationException { - serialize(streamWriter, instance); - } - - public static void serialize(SerializationStreamWriter streamWriter, AdminRole instance) - throws SerializationException { - } - - @Override - public boolean hasCustomInstantiateInstance() { - return true; - } - - @Override - public AdminRole instantiateInstance(SerializationStreamReader streamReader) throws SerializationException { - return instantiate(streamReader); - } - - public static AdminRole instantiate(SerializationStreamReader streamReader) throws SerializationException { - return AdminRole.getInstance(); - } - - @Override - public void deserializeInstance(SerializationStreamReader streamReader, AdminRole instance) - throws SerializationException { - deserialize(streamReader, instance); - } - - public static void deserialize(SerializationStreamReader streamReader, AdminRole instance) { - // Done by instantiateInstance - } - -} diff --git a/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java b/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java deleted file mode 100755 index 96625e142f0..00000000000 --- a/java/com.sap.sse.security.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sse/security/shared/UserRole_CustomFieldSerializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.google.gwt.user.client.rpc.core.com.sap.sse.security.shared; - -import com.google.gwt.user.client.rpc.CustomFieldSerializer; -import com.google.gwt.user.client.rpc.SerializationException; -import com.google.gwt.user.client.rpc.SerializationStreamReader; -import com.google.gwt.user.client.rpc.SerializationStreamWriter; -import com.sap.sse.security.shared.RolePrototype; -import com.sap.sse.security.shared.UserRole; - -public class UserRole_CustomFieldSerializer extends CustomFieldSerializer { - - @Override - public void serializeInstance(SerializationStreamWriter streamWriter, UserRole instance) - throws SerializationException { - serialize(streamWriter, instance); - } - - public static void serialize(SerializationStreamWriter streamWriter, UserRole instance) - throws SerializationException { - } - - @Override - public boolean hasCustomInstantiateInstance() { - return true; - } - - @Override - public UserRole instantiateInstance(SerializationStreamReader streamReader) throws SerializationException { - return instantiate(streamReader); - } - - public static UserRole instantiate(SerializationStreamReader streamReader) throws SerializationException { - return UserRole.getInstance(); - } - - @Override - public void deserializeInstance(SerializationStreamReader streamReader, UserRole instance) - throws SerializationException { - deserialize(streamReader, instance); - } - - public static void deserialize(SerializationStreamReader streamReader, RolePrototype instance) { - // Done by instantiateInstance - } - -} From 30f376232b97f4e88d1a8e67b3254718785da750 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 15:31:05 +0100 Subject: [PATCH 65/85] removed obsolete TODO --- .../sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index 997096b431c..04ae625c30d 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -491,7 +491,6 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { * If {@code raceColumnNames} is empty or {@code null}, return the names of all {@link raceColumnsOfLeaderboard}; * otherwise return those race column names from {@code raceColumnsOfLeaderboard} that are also in * {@code raceColumnNames}. - * @param leaderboard TODO * @param showOnlyActiveRacesForCompetitorIds * {@code null}, or specifies zero or more competitor IDs, requesting that the result contained the race * column holding that competitor's "active" race at the time requested; see also the From f35879729f9f6666913c94595acca9c68f3d8335 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 15:31:23 +0100 Subject: [PATCH 66/85] adding MarkProperties tags to the searchable/filterable strings --- .../gwt/ui/adminconsole/coursecreation/MarkPropertiesPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/coursecreation/MarkPropertiesPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/coursecreation/MarkPropertiesPanel.java index 209bc7266c4..2bba9919ec3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/coursecreation/MarkPropertiesPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/coursecreation/MarkPropertiesPanel.java @@ -110,6 +110,7 @@ public class MarkPropertiesPanel extends FlowPanel { strings.add(t.getName()); strings.add(t.getCommonMarkProperties().getShortName()); strings.add(t.getUuid().toString()); + Util.addAll(t.getTags(), strings); return strings; } From 3738dcef86bb4e727ac5274c71f30d5a0d4127e8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 11 Feb 2020 18:29:04 +0100 Subject: [PATCH 67/85] added more tests and adjusted the test certificate to make it injective --- .../resources/orc/newFormatCertificate.json | 2 +- .../domain/orc/TestORCPerformanceCurve.java | 21 ++++++++++++---- .../orc/impl/ORCPerformanceCurveImpl.java | 24 ++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json b/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json index f9c584edab4..253f280911d 100755 --- a/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json +++ b/java/com.sap.sailing.domain.test/resources/orc/newFormatCertificate.json @@ -159,7 +159,7 @@ 530.3, 516.8, 510.9, - 508.9 + 506.9 ], "R75":[ 697.1, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java index a466378199d..636fc8c0e63 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/orc/TestORCPerformanceCurve.java @@ -70,6 +70,16 @@ public class TestORCPerformanceCurve { } } + public void assertEquals(String message, double a, double b, double accuracy) { + try { + Assert.assertEquals(message, a, b, accuracy); + } catch (AssertionError e) { + if (collectErrors) { + collector.addError(e); + } + } + } + @BeforeClass public static void initialize() throws IOException, ParseException { List legs = new ArrayList<>(); @@ -253,15 +263,18 @@ public class TestORCPerformanceCurve { } @Test - public void testAllowancesAndImpliedWindForSpecificBins() throws FunctionEvaluationException { + public void testAllowancesAndImpliedWindForSpecificBins() throws FunctionEvaluationException, MaxIterationsExceededException { + final double highAccuracy = 0.01; + final double allowanceAccuracy = 0.1; final Distance ONE_NAUTICAL_MILE = new NauticalMileDistance(1.0); final ORCCertificate certificateWithSpecificBins = importerWithSpecificBins.getCertificateById("GRA00073GR317"); for (final Bearing twa : certificateWithSpecificBins.getTrueWindAngles()) { final ORCPerformanceCurveCourse singleLegOneMileCourseWithTwa = createSingleLegCourseWithTwa(twa); final ORCPerformanceCurve performanceCurveSpecificBins = new ORCPerformanceCurveImpl(certificateWithSpecificBins, singleLegOneMileCourseWithTwa); for (final Speed tws : certificateWithSpecificBins.getTrueWindSpeeds()) { - Assert.assertEquals(certificateWithSpecificBins.getVelocityPredictionPerTrueWindSpeedAndAngle().get(tws).get(twa).getDuration(ONE_NAUTICAL_MILE), - performanceCurveSpecificBins.getAllowancePerCourse(tws)); + final Duration duration = certificateWithSpecificBins.getVelocityPredictionPerTrueWindSpeedAndAngle().get(tws).get(twa).getDuration(ONE_NAUTICAL_MILE); + assertEquals("mismatch for twa "+twa+", tws "+tws, duration.asSeconds(), performanceCurveSpecificBins.getAllowancePerCourse(tws).asSeconds(), allowanceAccuracy); + assertEquals("mismatch for twa "+twa+", tws "+tws, tws.getKnots(), performanceCurveSpecificBins.getImpliedWind(duration).getKnots(), highAccuracy); } } } @@ -284,7 +297,7 @@ public class TestORCPerformanceCurve { list.add(new ORCPerformanceCurveLegImpl(new NauticalMileDistance(1), new DegreeBearingImpl(180))); ORCPerformanceCurveCourse complexCourse = new ORCPerformanceCurveCourseImpl(list); - double accuracy = 0.0001; + final double accuracy = 0.0001; ORCCertificate certificateMoana = importerLocal.getCertificateById("GER140772GER5549"); ORCCertificate certificateMilan = importerLocal.getCertificateById("GER166844GER7323"); ORCCertificate certificateTutima = importerLocal.getCertificateById("GER140618GER5609"); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java index 5b17b3d1458..124eadf303c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/orc/impl/ORCPerformanceCurveImpl.java @@ -71,6 +71,16 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv * allowance in sec/nm. */ private final UnivariateDifferentiableFunction functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse; + + /** + * The {@link ORCCertificate#getTrueWindAngles()} from the certificate + */ + private final Bearing[] trueWindAngles; + + /** + * The {@link ORCCertificate#getTrueWindSpeeds()} from the certificate + */ + private final Speed[] trueWindSpeeds; /** * Accepts the simplified polar data, one "column" for each of the defined true wind speeds, where each column is a @@ -79,6 +89,8 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv */ public ORCPerformanceCurveImpl(ORCCertificate certificate, ORCPerformanceCurveCourse course) throws FunctionEvaluationException { this.course = course; + this.trueWindAngles = certificate.getTrueWindAngles(); + this.trueWindSpeeds = certificate.getTrueWindSpeeds(); functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse = createPerformanceCurve(certificate); } @@ -251,14 +263,14 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv @Override public Speed getImpliedWind(Duration durationToCompleteCourse) throws MaxIterationsExceededException, FunctionEvaluationException{ final Speed averageSpeedOnCourse = getCourse().getTotalLength().inTime(durationToCompleteCourse); - final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS).mapToDouble + final double[] predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed = Arrays.stream(trueWindSpeeds).mapToDouble (tws->{ return functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse.value(tws.getKnots()); }).toArray(); final Speed result; // Corner cases for Allowance > Allowance(20kt) or Allowance < Allowance(6kt) if (averageSpeedOnCourse.getKnots() >= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed.length-1]) { - result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS.length-1]; + result = trueWindSpeeds[trueWindSpeeds.length-1]; } else if (averageSpeedOnCourse.equals(Speed.NULL) || averageSpeedOnCourse.getKnots() <= predictedSpeedsInKnotsForTotalCourseByTrueWindSpeed[0]) { - result = ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]; + result = trueWindSpeeds[0]; } else { // find the polynomial splined function that produces the durationToCompleteCourse within its validity range int i = 1; // skip the auxiliary spline segment from (0.0, 0.0) to (6.0, ...) @@ -272,7 +284,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv final UnivariateDifferentiableFunction targetZeroFunction = FunctionUtils.add(functionImpliedWindInKnotsToAverageSpeedInKnotsForCourse, FunctionUtils.multiply((UnivariateDifferentiableFunction) averageSpeedInKnots, (UnivariateDifferentiableFunction) new Constant(-1))); - final double impliedWindSpeedInKnots = newtonSolver.solve(1000, targetZeroFunction, 10 /* knots of true wind speed */); + final double impliedWindSpeedInKnots = newtonSolver.solve(1000, targetZeroFunction, 12 /* knots of true wind speed */); result = new KnotSpeedImpl(impliedWindSpeedInKnots); } return result; @@ -296,7 +308,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv Bearing trueWindAngle) throws FunctionEvaluationException, IllegalArgumentException { return getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(twaAllowances, beatAngles, beatVMGPredictionPerTrueWindSpeed, runAngles, runVMGPredictionPerTrueWindSpeed, trueWindSpeed, - trueWindAngle, ORCCertificate.ALLOWANCES_TRUE_WIND_ANGLES); + trueWindAngle, trueWindAngles); } public Speed getLagrangeSpeedPredictionForTrueWindSpeedAndAngle(Map> twaAllowances, @@ -341,7 +353,7 @@ public class ORCPerformanceCurveImpl implements Serializable, ORCPerformanceCurv */ private LinkedHashMap getAllowancesPerTrueWindSpeedsForCourse() throws ArgumentOutsideDomainException { final LinkedHashMap result = new LinkedHashMap<>(); - for (final Speed tws : ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS) { + for (final Speed tws : trueWindSpeeds) { result.put(tws, getAllowancePerCourse(tws)); } return result; From 9174fdac5f1e06b6f54cf1feadd7fce49dc0e3b8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 12 Feb 2020 00:28:11 +0100 Subject: [PATCH 68/85] added status and loadingProgress to the trackedRacesInfo in LeaderboardsResourceV2 --- .../server/gateway/jaxrs/api/AbstractLeaderboardsResource.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java index 62d4107f43c..c271e0aad43 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java @@ -185,6 +185,8 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer ? MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : new MillisecondsTimePoint(resultTimePoint))); trackedRaceInfo.put("delayToLiveInMillis", trackedRace.getDelayToLiveInMillis()); + trackedRaceInfo.put("status", trackedRace.getStatus().getStatus().toString()); + trackedRaceInfo.put("loadingProgress", trackedRace.getStatus().getLoadingProgress()); } fleetJson.put("trackedRace", trackedRaceInfo); } From 61e1d9894342b3cb1b8992b6fb5e14c39e7ede29 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 12 Feb 2020 09:24:16 +0100 Subject: [PATCH 69/85] added null check for new status attributes in AbstractLeaderboardResource --- .../gateway/jaxrs/api/AbstractLeaderboardsResource.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java index c271e0aad43..363d886e7fa 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/AbstractLeaderboardsResource.java @@ -185,8 +185,10 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer ? MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : new MillisecondsTimePoint(resultTimePoint))); trackedRaceInfo.put("delayToLiveInMillis", trackedRace.getDelayToLiveInMillis()); - trackedRaceInfo.put("status", trackedRace.getStatus().getStatus().toString()); - trackedRaceInfo.put("loadingProgress", trackedRace.getStatus().getLoadingProgress()); + if (trackedRace.getStatus() != null) { + trackedRaceInfo.put("status", trackedRace.getStatus().getStatus().toString()); + trackedRaceInfo.put("loadingProgress", trackedRace.getStatus().getLoadingProgress()); + } } fleetJson.put("trackedRace", trackedRaceInfo); } From e5b61b69f5db6bc1382e43f813a31940c4424f86 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 12 Feb 2020 10:44:14 +0100 Subject: [PATCH 70/85] bug5204: reverting regression effects of commit 5afaa1d4505d39372250ec1b11b8803e3271de6b; trying to resize the ManeuverTable leads to a recursive resize effect that eventually downsizes the maneuver table widget to height 0; temporarily disabling the resize until a working solution has been found. The effect will be that the resize handles may be pushed out of view, e.g., when shrinking the containing window or when rotating a small touch screen, hence changing aspect ratio. --- .../gwt/ui/raceboard/TouchSplitLayoutPanel.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java index b861d4fddeb..62772a88d2f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java @@ -38,7 +38,6 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.ProvidesResize; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.WidgetCollection; @@ -202,7 +201,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { * * @author Simon Marcel Pamies, Axel Uhl */ - abstract class Splitter extends AbsolutePanel implements RequiresResize, ProvidesResize { + abstract class Splitter extends AbsolutePanel { protected final Widget target; private int offset; @@ -319,7 +318,6 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { syncedCenterSize = newCenterSize; centerSize = newCenterSize; } - return Math.max(((LayoutData) target.getLayoutData()).size + centerSize, 0); } @@ -376,11 +374,6 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { } } - @Override - public void onResize() { - setAssociatedWidgetSize(getTargetSize(), /* defer */ false); - } - protected abstract int getAbsolutePosition(); protected abstract double getCenterSize(); protected abstract int getEventPosition(Event event); @@ -851,7 +844,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { if (splitter != null) { LayoutData layoutData = (LayoutData) widget.getLayoutData(); if (hidden) { - if(layoutData.size > 0) { + if (layoutData.size > 0) { layoutData.oldSize = layoutData.size; } widget.setVisible(false); @@ -861,7 +854,6 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { } } splitter.setAssociatedWidgetSize(0, /* defer */false); - // need to ensure visibility of dragger, do NOT use setVisible() as south/north splitter should be shown always splitter.setDraggerVisible(!hidden); } else { From eea21259d05221045e62a7145317a08509b8a419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Hessenm=C3=BCller?= Date: Wed, 12 Feb 2020 15:55:56 +0100 Subject: [PATCH 71/85] Revert "bug5204: reverting regression effects of commit 5afaa1d4505d39372250ec1b11b8803e3271de6b;" This reverts commit e5b61b69f5db6bc1382e43f813a31940c4424f86. --- .../gwt/ui/raceboard/TouchSplitLayoutPanel.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java index 62772a88d2f..b861d4fddeb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java @@ -38,6 +38,7 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.ProvidesResize; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.WidgetCollection; @@ -201,7 +202,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { * * @author Simon Marcel Pamies, Axel Uhl */ - abstract class Splitter extends AbsolutePanel { + abstract class Splitter extends AbsolutePanel implements RequiresResize, ProvidesResize { protected final Widget target; private int offset; @@ -318,6 +319,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { syncedCenterSize = newCenterSize; centerSize = newCenterSize; } + return Math.max(((LayoutData) target.getLayoutData()).size + centerSize, 0); } @@ -374,6 +376,11 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { } } + @Override + public void onResize() { + setAssociatedWidgetSize(getTargetSize(), /* defer */ false); + } + protected abstract int getAbsolutePosition(); protected abstract double getCenterSize(); protected abstract int getEventPosition(Event event); @@ -844,7 +851,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { if (splitter != null) { LayoutData layoutData = (LayoutData) widget.getLayoutData(); if (hidden) { - if (layoutData.size > 0) { + if(layoutData.size > 0) { layoutData.oldSize = layoutData.size; } widget.setVisible(false); @@ -854,6 +861,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { } } splitter.setAssociatedWidgetSize(0, /* defer */false); + // need to ensure visibility of dragger, do NOT use setVisible() as south/north splitter should be shown always splitter.setDraggerVisible(!hidden); } else { From 8a37161c6e7f5fb20b8d38315ec81e0fbaa4024a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Hessenm=C3=BCller?= Date: Wed, 12 Feb 2020 15:59:08 +0100 Subject: [PATCH 72/85] Changed margin to padding in ManeuverTablePanel.css so that it is included in Element.offsetHeight --- .../ui/client/shared/racemap/maneuver/ManeuverTablePanel.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.css index d03e6800514..3508f5b62ef 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.css @@ -1,6 +1,6 @@ .maneuverPanel { - margin-top: 1em; - margin-right: 3em; + padding-top: 1em; + padding-right: 3em; overflow: auto; } .contentContainer { From 3bddfad0aa222bb74395b2fa1ceb14d4ef41533b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Hessenm=C3=BCller?= Date: Wed, 12 Feb 2020 16:00:37 +0100 Subject: [PATCH 73/85] Fixed bug showing the edit dialog on resize in TaggingPanel.java --- .../sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java index 66a007faeba..975681a438c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/tagging/TaggingPanel.java @@ -196,7 +196,9 @@ public class TaggingPanel extends ComponentWithoutSettings @Override public void onResize() { super.onResize(); - taggingPanel.setWidgetSize(footerPanel, calculateFooterPanelHeight()); + if (currentState != State.VIEW) { + taggingPanel.setWidgetSize(footerPanel, calculateFooterPanelHeight()); + } } }; filterbarAndContentPanel = new HeaderPanel(); From 762c64e0052bc00f1113a8a797afcf08dd6449b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Hessenm=C3=BCller?= Date: Wed, 12 Feb 2020 16:03:35 +0100 Subject: [PATCH 74/85] Bug5204: Removed unnecessary recursion in TouchSplitLayoutPanel.java --- .../ui/raceboard/TouchSplitLayoutPanel.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java index b861d4fddeb..f1664ec62ab 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/TouchSplitLayoutPanel.java @@ -145,14 +145,14 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { LayoutData layout = (LayoutData) associatedSplitter.target.getLayoutData(); if (layout.size == 0) { // Restore the old size. - associatedSplitter.setAssociatedWidgetSize(layout.oldSize, /* defer */true); + associatedSplitter.setAssociatedWidgetSize(layout.oldSize, /* forceLayout */true); } else { /* * Collapse to size 0. We change the size instead of hiding the widget because hiding the * widget can cause issues if the widget contains a flash component. */ layout.oldSize = layout.size; - associatedSplitter.setAssociatedWidgetSize(0, /* defer */true); + associatedSplitter.setAssociatedWidgetSize(0, /* forceLayout */true); } } associatedSplitter.lastClick = now; @@ -175,7 +175,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { - associatedSplitter.offset; } ((LayoutData) associatedSplitter.target.getLayoutData()).hidden = false; - associatedSplitter.setAssociatedWidgetSize(size, /* defer */true); + associatedSplitter.setAssociatedWidgetSize(size, /* forceLayout */true); event.preventDefault(); } break; @@ -294,7 +294,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { // Try resetting the associated widget's size, which will enforce the new // minSize value. - setAssociatedWidgetSize((int) layout.size, /* defer */true); + setAssociatedWidgetSize((int) layout.size, /* forceLayout */true); } public void setSnapClosedSize(int snapClosedSize) { @@ -327,10 +327,10 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { * Sets the size of the associated {@link Widget} thus moving it down, up, left or right * depending of the position. Informs the {@link DockLayoutPanel} about the change. * - * @param defer if set to true then the layout change will be deferred until the browser + * @param forceLayout if set to true then the layout change will be deferred until the browser * event loop returns. */ - private void setAssociatedWidgetSize(double size, boolean defer) { + private void setAssociatedWidgetSize(double size, boolean forceLayout) { double maxSize = getMaxSize(); if (size > maxSize) { size = maxSize; @@ -353,7 +353,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { // Defer actually updating the layout, so that if we receive many // mouse events before layout/paint occurs, we'll only update once. - if (layoutCommand == null && defer) { + if (layoutCommand == null && forceLayout) { layoutCommand = new ScheduledCommand() { @Override public void execute() { @@ -362,9 +362,6 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { } }; Scheduler.get().scheduleDeferred(layoutCommand); - } else { - // force layout on DockPanel - forceLayout(); } if (getAssociatedComponent() instanceof RequiresResize) { Scheduler.get().scheduleFinally(new ScheduledCommand() { @@ -378,7 +375,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { @Override public void onResize() { - setAssociatedWidgetSize(getTargetSize(), /* defer */ false); + setAssociatedWidgetSize(getTargetSize(), /* forceLayout */ false); } protected abstract int getAbsolutePosition(); @@ -860,7 +857,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { associatedComponentToWidget.setVisible(false); } } - splitter.setAssociatedWidgetSize(0, /* defer */false); + splitter.setAssociatedWidgetSize(0, /* forceLayout */false); // need to ensure visibility of dragger, do NOT use setVisible() as south/north splitter should be shown always splitter.setDraggerVisible(!hidden); @@ -875,10 +872,10 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { //it is not yet sized if(layoutData.oldSize > 0) { //and we have a stored old size it had before minimizing it - splitter.setAssociatedWidgetSize(layoutData.oldSize, /* defer */false); + splitter.setAssociatedWidgetSize(layoutData.oldSize, /* forceLayout */false); } else { //and we do not have any size yet, so use the default size - splitter.setAssociatedWidgetSize(initialSize, /* defer */false); + splitter.setAssociatedWidgetSize(initialSize, /* forceLayout */false); } } splitter.setDraggerVisible(!hidden); @@ -902,7 +899,7 @@ public class TouchSplitLayoutPanel extends DockLayoutPanel { final Splitter splitter = getAssociatedSplitter(widget); if (splitter != null) { if (widget.isVisible()) { - splitter.setAssociatedWidgetSize(size, /* defer */false); + splitter.setAssociatedWidgetSize(size, /* forceLayout */false); } else { ((LayoutData) widget.getLayoutData()).oldSize = size; } From 2b1edbd243a0eb911ebdd9ce26db973f8fc82482 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 12 Feb 2020 16:39:31 +0100 Subject: [PATCH 75/85] added a missing @Override for new UserStoreImpl method --- .../com/sap/sse/security/userstore/mongodb/UserStoreImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java index ed5b30f6e3e..a64472623fc 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java @@ -291,6 +291,7 @@ public class UserStoreImpl implements UserStore { return roleDefinition; } + @Override public RoleDefinition getRoleDefinitionByPrototype(RolePrototype rolePrototype) { final RoleDefinition roleDefinition = getRoleDefinition(rolePrototype.getId()); if (roleDefinition == null) { From 25ceed80827e94d07d636638a2458359774af4f4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 12 Feb 2020 23:00:30 +0100 Subject: [PATCH 76/85] made Expedition/UDP receivers more robust, logging exceptions in listeners instead of dying --- .../impl/ExpeditionMessageImpl.java | 25 +++++++++++++------ .../sap/sailing/udpconnector/UDPReceiver.java | 9 ++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java index 14046090bea..b1a1fa33653 100755 --- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java +++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java @@ -165,31 +165,40 @@ public class ExpeditionMessageImpl implements ExpeditionMessage { @Override public Bearing getTrueWindBearing() { + final Bearing result; if (hasValue(ID_TWD)) { // TWD represents the "from" direction and need to be reversed to obtain the "to" bearing - return new DegreeBearingImpl(getValue(ID_TWD)).reverse(); + result = new DegreeBearingImpl(getValue(ID_TWD)).reverse(); } else if (hasValue(ID_GWD)) { - return new DegreeBearingImpl(getValue(ID_GWD)).reverse(); + result = new DegreeBearingImpl(getValue(ID_GWD)).reverse(); } else { - return null; + result = null; } + return result; } @Override public SpeedWithBearing getSpeedOverGround() { - if (hasValue(ID_GPS_COG) && hasValue(ID_GPS_SOG)) { - return new KnotSpeedWithBearingImpl(getValue(ID_GPS_SOG), getCourseOverGround()); + final SpeedWithBearing result; + if (hasValue(ID_GPS_COG)) { + // We're assuming that if SOG is not part of the message but COG is then + // the SOG was probably so low that it's fair to approximate it as 0. + final double sogInKnots = hasValue(ID_GPS_SOG) ? getValue(ID_GPS_SOG) : 0; + result = new KnotSpeedWithBearingImpl(sogInKnots, getCourseOverGround()); } else { - return null; + result = null; } + return result; } @Override public Bearing getCourseOverGround() { + final Bearing result; if (hasValue(ID_GPS_COG)) { - return new DegreeBearingImpl(getValue(ID_GPS_COG)); + result = new DegreeBearingImpl(getValue(ID_GPS_COG)); } else { - return null; + result = null; } + return result; } @Override diff --git a/java/com.sap.sailing.udpconnector/src/com/sap/sailing/udpconnector/UDPReceiver.java b/java/com.sap.sailing.udpconnector/src/com/sap/sailing/udpconnector/UDPReceiver.java index cf7ad3bddf2..edf81650554 100755 --- a/java/com.sap.sailing.udpconnector/src/com/sap/sailing/udpconnector/UDPReceiver.java +++ b/java/com.sap.sailing.udpconnector/src/com/sap/sailing/udpconnector/UDPReceiver.java @@ -62,12 +62,19 @@ public abstract class UDPReceiver Date: Wed, 12 Feb 2020 23:57:31 +0100 Subject: [PATCH 77/85] return a null GPSFixMoving if no reasonable speed object can be assembled from the Expedition message --- .../impl/ExpeditionMessageImpl.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java index b1a1fa33653..9648504000c 100755 --- a/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java +++ b/java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/ExpeditionMessageImpl.java @@ -144,23 +144,32 @@ public class ExpeditionMessageImpl implements ExpeditionMessage { @Override public GPSFixMoving getGPSFixMoving() { + final GPSFixMoving result; if (hasValue(ID_GPS_LAT) && hasValue(ID_GPS_LNG)) { - return new GPSFixMovingImpl(new DegreePosition(getValue(ID_GPS_LAT), getValue(ID_GPS_LNG)), getTimePoint(), - getSpeedOverGround()); + final SpeedWithBearing cogSog = getSpeedOverGround(); + if (cogSog != null) { + result = new GPSFixMovingImpl(new DegreePosition(getValue(ID_GPS_LAT), getValue(ID_GPS_LNG)), getTimePoint(), + getSpeedOverGround()); + } else { + result = null; + } } else { - return null; + result = null; } + return result; } @Override public SpeedWithBearing getTrueWind() { + final SpeedWithBearing result; if (hasValue(ID_TWD) && hasValue(ID_TWS)) { - return new KnotSpeedWithBearingImpl(getValue(ID_TWS), getTrueWindBearing()); + result = new KnotSpeedWithBearingImpl(getValue(ID_TWS), getTrueWindBearing()); } else if (hasValue(ID_GWD) && hasValue(ID_GWS)) { - return new KnotSpeedWithBearingImpl(getValue(ID_GWS), getTrueWindBearing()); + result = new KnotSpeedWithBearingImpl(getValue(ID_GWS), getTrueWindBearing()); } else { - return null; + result = null; } + return result; } @Override From 5b5dc7862e067bb29d84d616f1e94e9a1ac4dbe9 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 13 Feb 2020 13:13:41 +0100 Subject: [PATCH 78/85] Added missing Serializable as well as serialVersionUIDs --- .../com/sap/sailing/domain/coursetemplate/Positioning.java | 4 +++- .../domain/coursetemplate/impl/FixedPositioningImpl.java | 2 ++ .../impl/TrackingDeviceBasedPositioningImpl.java | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/Positioning.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/Positioning.java index b720f6154ea..a5014f59579 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/Positioning.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/Positioning.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.coursetemplate; +import java.io.Serializable; + import com.sap.sailing.domain.common.DeviceIdentifier; /** @@ -9,6 +11,6 @@ import com.sap.sailing.domain.common.DeviceIdentifier; * @author Axel Uhl (D043530) * */ -public interface Positioning { +public interface Positioning extends Serializable { T accept(PositioningVisitor visitor); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FixedPositioningImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FixedPositioningImpl.java index 683f3b98f7e..3981171688c 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FixedPositioningImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/FixedPositioningImpl.java @@ -4,6 +4,8 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.coursetemplate.FixedPositioning; public class FixedPositioningImpl implements FixedPositioning { + private static final long serialVersionUID = 267606077936062645L; + private final Position fixedPosition; public FixedPositioningImpl(Position fixedPosition) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/TrackingDeviceBasedPositioningImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/TrackingDeviceBasedPositioningImpl.java index 99c8fd65a23..9b2e63b1ed8 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/TrackingDeviceBasedPositioningImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/coursetemplate/impl/TrackingDeviceBasedPositioningImpl.java @@ -4,6 +4,8 @@ import com.sap.sailing.domain.common.DeviceIdentifier; import com.sap.sailing.domain.coursetemplate.TrackingDeviceBasedPositioning; public class TrackingDeviceBasedPositioningImpl implements TrackingDeviceBasedPositioning { + private static final long serialVersionUID = -4059477014731789050L; + private final DeviceIdentifier deviceIdentifier; public TrackingDeviceBasedPositioningImpl(DeviceIdentifier deviceIdentifier) { From 5f2515c3d7d5bd3c4ea8ea284a8147cfe6820777 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Thu, 13 Feb 2020 13:24:18 +0100 Subject: [PATCH 79/85] Added launch configuration that connects to localhost:8888 as replica for SecurityService and SharedSailingData only --- ...on 8889, auto-replicate SS and SSD).launch | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate SS and SSD).launch diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate SS and SSD).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate SS and SSD).launch new file mode 100644 index 00000000000..f57bb850e50 --- /dev/null +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, auto-replicate SS and SSD).launch @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 19e38bca458a4ad9e76ec8a0e6c5f0381e8f98ee Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 16:18:18 +0100 Subject: [PATCH 80/85] added LEG_WINDWARD_DISTANCE_TO_GO_IN_METERS which was already implemented to the list of supported detail types in LeaderboardsResourceV2 --- .../server/gateway/jaxrs/api/LeaderboardsResourceV2.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java index 04ae625c30d..577e78c5610 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResourceV2.java @@ -311,7 +311,8 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, DetailType.NUMBER_OF_MANEUVERS, DetailType.RACE_CURRENT_LEG, - DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS }; + DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, + DetailType.LEG_WINDWARD_DISTANCE_TO_GO_IN_METERS }; } private DetailType[] getAvailableOverallDetailColumnTypes() { From 75bd23f84022e353ef18d0ed3006938e83e3793e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 17:36:44 +0100 Subject: [PATCH 81/85] fixed bug: initial server group creation must not use "store" as captured object in the operation; otherwise, the entire SecurityServiceImpl object will be serialized which fails --- .../src/com/sap/sse/security/impl/SecurityServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 81033ca5f3a..287778d5bcb 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 @@ -2086,7 +2086,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat store.replaceContentsFrom(newUserStore); if (newServerGroup == null) { // create the server group in a replication-aware fashion, making sure it appears on the master - apply(s->s.internalCreateUserGroup(UUID.randomUUID(), store.getServerGroupName())); + final String serverGroupName = store.getServerGroupName(); + apply(s->s.internalCreateUserGroup(UUID.randomUUID(), serverGroupName)); store.setServerGroup(store.getUserGroupByName(store.getServerGroupName())); } else if (newServerGroup != oldServerGroup) { store.setServerGroup(newServerGroup); From ab11635966c31e6680b174ec56f55adc8560eb7c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 17:44:32 +0100 Subject: [PATCH 82/85] compute the UUID for the new server group once and not as part of the create operation --- .../src/com/sap/sse/security/impl/SecurityServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 287778d5bcb..abe2826ea57 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 @@ -2087,7 +2087,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat if (newServerGroup == null) { // create the server group in a replication-aware fashion, making sure it appears on the master final String serverGroupName = store.getServerGroupName(); - apply(s->s.internalCreateUserGroup(UUID.randomUUID(), serverGroupName)); + final UUID serverGroupUuid = UUID.randomUUID(); + apply(s->s.internalCreateUserGroup(serverGroupUuid, serverGroupName)); store.setServerGroup(store.getUserGroupByName(store.getServerGroupName())); } else if (newServerGroup != oldServerGroup) { store.setServerGroup(newServerGroup); From 3a2a1c947ad2a60912b7fe4d25c35c77d4b953b3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 17:50:10 +0100 Subject: [PATCH 83/85] re-use existing method to create a user group in SecurityServiceImpl with replication --- .../src/com/sap/sse/security/impl/SecurityServiceImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 abe2826ea57..be20908a271 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 @@ -667,8 +667,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat return createUserGroupWithInitialUser(id, name, getCurrentUser()); } - private UserGroup createUserGroupWithInitialUser(UUID id, String name, User initialUser) - throws UserGroupManagementException { + private UserGroup createUserGroupWithInitialUser(UUID id, String name, User initialUser) { logger.info("Creating user group " + name + " with ID " + id); apply(s -> s.internalCreateUserGroup(id, name)); final UserGroup userGroup = store.getUserGroup(id); @@ -2088,7 +2087,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat // create the server group in a replication-aware fashion, making sure it appears on the master final String serverGroupName = store.getServerGroupName(); final UUID serverGroupUuid = UUID.randomUUID(); - apply(s->s.internalCreateUserGroup(serverGroupUuid, serverGroupName)); + createUserGroupWithInitialUser(serverGroupUuid, serverGroupName, /* initial user */ null); store.setServerGroup(store.getUserGroupByName(store.getServerGroupName())); } else if (newServerGroup != oldServerGroup) { store.setServerGroup(newServerGroup); From d19bd2a7f3ed6697bd23d4fc4aa5d1bfced175da Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 18:41:31 +0100 Subject: [PATCH 84/85] "migrate" SERVER object also after receiving initial load, ensuring that a newly created server group is used for the SERVER object's group ownership --- .../ui/adminconsole/LocalServerManagementPanel.java | 2 -- .../com/sap/sse/security/ui/client/UserService.java | 1 - .../src/com/sap/sse/security/SecurityService.java | 7 +++++++ .../src/com/sap/sse/security/impl/Activator.java | 5 +---- .../com/sap/sse/security/impl/SecurityServiceImpl.java | 10 ++++++++++ 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java index 9ebea4f1f7e..02edf7d669e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LocalServerManagementPanel.java @@ -161,7 +161,6 @@ public class LocalServerManagementPanel extends SimplePanel { final boolean hasUserOwner = ownership != null && ownership.getUserOwner() != null; groupOwnerInfo.setText(hasGroupOwner ? ownership.getTenantOwner().getName() : "---"); userOwnerInfo.setText(hasUserOwner ? ownership.getUserOwner().getName() : "---"); - // Update changeability isSelfServiceServerCheckbox.setEnabled(userService.hasCurrentUserMetaPermission( serverInfo.getIdentifier().getPermission(ServerActions.CREATE_OBJECT), serverInfo.getOwnership())); @@ -172,7 +171,6 @@ public class LocalServerManagementPanel extends SimplePanel { private void updateServerConfiguration(ServerConfigurationDTO result) { isStandaloneServerCheckbox.setValue(result.isStandaloneServer(), false); isStandaloneServerCheckbox.setEnabled(true); - isPublicServerCheckbox.setValue(result.isPublic(), false); isSelfServiceServerCheckbox.setValue(result.isSelfService(), false); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java index 553b41ee4c0..972529d2ecd 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserService.java @@ -271,7 +271,6 @@ public class UserService { } anonymousUser = resultAndAnomynous.getB(); serverInfo = resultAndAnomynous.getC(); - preAuthenticated = (!userInitiallyLoaded && currentUser != null); userInitiallyLoaded = true; logger.info("User changed to " 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 2a3ea204a7a..a4a8334fc59 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 @@ -535,6 +535,13 @@ public interface SecurityService extends ReplicableWithObjectInputStream permissionReplacement); + /** + * If the {@link SecuredSecurityTypes#SERVER} object has a group ownership. If not, it is set to the + * {@link #getServerGroup() server group}. The {@link SecuredSecurityTypes#SERVER} type is then marked + * as migrated (see {@link #checkMigration(Iterable)}). + */ + void migrateServerObject(); + void checkMigration(Iterable allInstances); boolean hasCurrentUserRoleForOwnedObject(HasPermissions type, T object, 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 1c46ea379ad..2ff62716959 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 @@ -280,10 +280,7 @@ public class Activator implements BundleActivator { } } } - final QualifiedObjectIdentifier serverIdentifier = SecuredSecurityTypes.SERVER - .getQualifiedObjectIdentifier( - new TypeRelativeObjectIdentifier(ServerInfo.getName())); - securityService.migrateOwnership(serverIdentifier, serverIdentifier.toString()); + securityService.migrateServerObject(); securityService.checkMigration(SecuredSecurityTypes.getAllInstances()); } 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 be20908a271..d3f082d3d56 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 @@ -2105,6 +2105,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat } finally { Thread.currentThread().setContextClassLoader(oldCCL); } + // now ensure that the SERVER object has a valid ownership; use the (potentially new) server group as the default: + migrateServerObject(); logger.info("Reading isSharedAcrossSubdomains..."); sharedAcrossSubdomainsOf = (String) is.readObject(); logger.info("...as "+sharedAcrossSubdomainsOf); @@ -2251,6 +2253,14 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat return wasNecessaryToMigrate; } + @Override + public void migrateServerObject() { + final QualifiedObjectIdentifier serverIdentifier = SecuredSecurityTypes.SERVER + .getQualifiedObjectIdentifier( + new TypeRelativeObjectIdentifier(ServerInfo.getName())); + migrateOwnership(serverIdentifier, serverIdentifier.toString()); + } + @Override public void checkMigration(Iterable allInstances) { Class clazz = Util.first(allInstances).getClass(); From ccbd5ce6f5b1416a935fc888d12d08c9d6dc77c3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 13 Feb 2020 23:41:19 +0100 Subject: [PATCH 85/85] improved admin release notes --- java/com.sap.sailing.www/release_notes_admin.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 5fd30f111a4..ebd2258e16b 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -24,7 +24,8 @@

      February 2020

      • The "Local Server" tab now refreshes automatically when it comes into view.
      • -
      • There is a new system property repliate.restore.replicas. When it is set to true, +
      • There is a new system property repliate.restore.replicas. When it is set to true + (which is the default), upon start-up the server instance will read the replica descriptors stored persistently when a replica registers, assuming that those replicas are still alive and want to continue to receive replication updates from this server. This way, replicas may not have to be