build.gradle
... ...
@@ -13,7 +13,7 @@ buildscript {
13 13
}
14 14
}
15 15
dependencies {
16
- classpath 'com.android.tools.build:gradle:3.6.0'
16
+ classpath 'com.android.tools.build:gradle:3.6.1'
17 17
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.3"
18 18
classpath "org.ajoberstar:grgit:2.3.0"
19 19
}
gradle.properties
... ...
@@ -21,8 +21,8 @@ org.gradle.jvmargs=-Xmx4096m
21 21
22 22
commonRepoURL=http://nexus.wdf.sap.corp:8081/nexus/content/repositories/build.snapshots
23 23
sapGradleDistBaseUrl=http://nexus.wdf.sap.corp:8081/nexus/content/repositories/build.releases/org/gradle/download/gradle/gradle
24
-sapGradleVersion=4.6
24
+sapGradleVersion=6.0.1
25 25
sapProxyHost=proxy.wdf.sap.corp
26 26
sapProxyPort=8080
27 27
sapNonProxyHosts=nexus.wdf.sap.corp
28
-group=com.sap.sailing.android
... ...
\ No newline at end of file
0
+group=com.sap.sailing.android
gradle/wrapper/gradle-wrapper.properties
... ...
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
3 3
distributionPath=wrapper/dists
4 4
zipStoreBase=GRADLE_USER_HOME
5 5
zipStorePath=wrapper/dists
6
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
6
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF
... ...
@@ -23,7 +23,8 @@ Require-Bundle: com.sap.sailing.domain,
23 23
com.sap.sse.gwt.adminconsole,
24 24
org.apache.commons.math;bundle-version="2.1.0",
25 25
com.sap.sse.security.ui,
26
- com.sap.sse
26
+ com.sap.sse,
27
+ com.sap.sse.replication.interfaces
27 28
Bundle-ActivationPolicy: lazy
28 29
Import-Package: com.sap.sse.security.shared.dto,
29 30
javax.servlet;version="3.1.0",
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchServlet.java
... ...
@@ -15,31 +15,44 @@ import com.sap.sse.gwt.dispatch.client.transport.gwtrpc.RequestWrapper;
15 15
import com.sap.sse.gwt.dispatch.servlets.AbstractDispatchServlet;
16 16
import com.sap.sse.gwt.dispatch.shared.commands.Action;
17 17
import com.sap.sse.gwt.dispatch.shared.commands.Result;
18
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
19
+import com.sap.sse.replication.ReplicationService;
18 20
import com.sap.sse.util.ServiceTrackerFactory;
19 21
20 22
public class DashboardDispatchServlet extends AbstractDispatchServlet<DashboardDispatchContext> {
21 23
private static final long serialVersionUID = -245230476512348999L;
22 24
23
- private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
25
+ private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
24 26
private final PolarDataService polarDataService;
25 27
private final DashboardLiveRaceProvider dashboardLiveRaceProvider;
26 28
private final MovingAveragesCache movingAveragesCache;
27 29
28 30
public DashboardDispatchServlet() {
29 31
final BundleContext context = Activator.getDefault();
30
- racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
32
+ final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker =
33
+ ServiceTrackerFactory.createAndOpen(context, ReplicationService.class);
34
+ racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class,
35
+ /* customizer */ null, replicationServiceTracker);
36
+ racingEventServiceTracker.open();
31 37
polarDataService = getPolarDataService();
32
- dashboardLiveRaceProvider = new DashboardLiveRaceProvider(racingEventServiceTracker.getService());
38
+ try {
39
+ dashboardLiveRaceProvider = new DashboardLiveRaceProvider(racingEventServiceTracker.getInitializedService(0));
40
+ } catch (InterruptedException e) {
41
+ throw new RuntimeException(e);
42
+ }
33 43
movingAveragesCache = new MovingAveragesCache();
34 44
}
35 45
36 46
@Override
37 47
protected <R extends Result, A extends Action<R, DashboardDispatchContext>> DashboardDispatchContext createDispatchContextFor(
38 48
RequestWrapper<R, A, DashboardDispatchContext> request) {
39
- return new DashboardDispatchContextImpl(request.getCurrentClientTime(),
40
- racingEventServiceTracker.getService(),
41
- polarDataService, dashboardLiveRaceProvider, movingAveragesCache, request.getClientLocaleName(),
42
- getThreadLocalRequest());
49
+ try {
50
+ return new DashboardDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getInitializedService(0),
51
+ polarDataService, dashboardLiveRaceProvider, movingAveragesCache, request.getClientLocaleName(),
52
+ getThreadLocalRequest());
53
+ } catch (InterruptedException e) {
54
+ throw new RuntimeException(e);
55
+ }
43 56
}
44 57
45 58
private PolarDataService getPolarDataService() {
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java
... ...
@@ -60,8 +60,18 @@ public class LegEntryDTO implements Serializable {
60 60
public boolean finished;
61 61
public Map<ManeuverType, Integer> numberOfManeuvers;
62 62
public Map<ManeuverType, Double> averageManeuverLossInMeters;
63
- public Double averageAbsoluteCrossTrackErrorInMeters;
64
- public Double averageSignedCrossTrackErrorInMeters;
63
+
64
+ /**
65
+ * The absolute cross track error; averaged when asked for a time point after the competitor has finished the leg,
66
+ * or the current value when in the leg; {@code null} when not yet in the leg.
67
+ */
68
+ public Double currentOrAverageAbsoluteCrossTrackErrorInMeters;
69
+
70
+ /**
71
+ * The signed cross track error; averaged when asked for a time point after the competitor has finished the leg,
72
+ * or the current value when in the leg; {@code null} when not yet in the leg.
73
+ */
74
+ public Double currentOrAverageSignedCrossTrackErrorInMeters;
65 75
66 76
/**
67 77
* The corrected time spent since the start of the race up to the current time point or the finishing of
... ...
@@ -75,11 +85,11 @@ public class LegEntryDTO implements Serializable {
75 85
public int hashCode() {
76 86
final int prime = 31;
77 87
int result = 1;
78
- result = prime * result + ((averageAbsoluteCrossTrackErrorInMeters == null) ? 0
79
- : averageAbsoluteCrossTrackErrorInMeters.hashCode());
88
+ result = prime * result + ((currentOrAverageAbsoluteCrossTrackErrorInMeters == null) ? 0
89
+ : currentOrAverageAbsoluteCrossTrackErrorInMeters.hashCode());
80 90
result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode());
81
- result = prime * result + ((averageSignedCrossTrackErrorInMeters == null) ? 0
82
- : averageSignedCrossTrackErrorInMeters.hashCode());
91
+ result = prime * result + ((currentOrAverageSignedCrossTrackErrorInMeters == null) ? 0
92
+ : currentOrAverageSignedCrossTrackErrorInMeters.hashCode());
83 93
result = prime * result
84 94
+ ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode());
85 95
result = prime * result + ((correctedTotalTime == null) ? 0 : correctedTotalTime.hashCode());
... ...
@@ -124,20 +134,20 @@ public class LegEntryDTO implements Serializable {
124 134
if (getClass() != obj.getClass())
125 135
return false;
126 136
LegEntryDTO other = (LegEntryDTO) obj;
127
- if (averageAbsoluteCrossTrackErrorInMeters == null) {
128
- if (other.averageAbsoluteCrossTrackErrorInMeters != null)
137
+ if (currentOrAverageAbsoluteCrossTrackErrorInMeters == null) {
138
+ if (other.currentOrAverageAbsoluteCrossTrackErrorInMeters != null)
129 139
return false;
130
- } else if (!averageAbsoluteCrossTrackErrorInMeters.equals(other.averageAbsoluteCrossTrackErrorInMeters))
140
+ } else if (!currentOrAverageAbsoluteCrossTrackErrorInMeters.equals(other.currentOrAverageAbsoluteCrossTrackErrorInMeters))
131 141
return false;
132 142
if (averageManeuverLossInMeters == null) {
133 143
if (other.averageManeuverLossInMeters != null)
134 144
return false;
135 145
} else if (!averageManeuverLossInMeters.equals(other.averageManeuverLossInMeters))
136 146
return false;
137
- if (averageSignedCrossTrackErrorInMeters == null) {
138
- if (other.averageSignedCrossTrackErrorInMeters != null)
147
+ if (currentOrAverageSignedCrossTrackErrorInMeters == null) {
148
+ if (other.currentOrAverageSignedCrossTrackErrorInMeters != null)
139 149
return false;
140
- } else if (!averageSignedCrossTrackErrorInMeters.equals(other.averageSignedCrossTrackErrorInMeters))
150
+ } else if (!currentOrAverageSignedCrossTrackErrorInMeters.equals(other.currentOrAverageSignedCrossTrackErrorInMeters))
141 151
return false;
142 152
if (averageSpeedOverGroundInKnots == null) {
143 153
if (other.averageSpeedOverGroundInKnots != null)
java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF
... ...
@@ -25,7 +25,8 @@ Require-Bundle: org.json.simple;bundle-version="1.1.0",
25 25
com.sap.sse.security.common,
26 26
com.sap.sse.security.interface,
27 27
org.apache.shiro.core,
28
- org.apache.shiro.web
28
+ org.apache.shiro.web,
29
+ com.sap.sse.replication
29 30
Import-Package: com.sap.sse.security.shared,
30 31
javax.ws.rs;version="1.1.1",
31 32
javax.ws.rs.core;version="1.1.1",
java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java
32 33
index 16cf9cf..d677e21
33
--- a/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java
34
+++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java
... ...
@@ -10,6 +10,8 @@ import org.osgi.util.tracker.ServiceTracker;
10 10
11 11
import com.sap.sailing.domain.igtimiadapter.Client;
12 12
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
13
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
14
+import com.sap.sse.replication.ReplicationService;
13 15
import com.sap.sse.security.SecurityService;
14 16
import com.sap.sse.util.ServiceTrackerFactory;
15 17
... ...
@@ -26,7 +28,7 @@ import com.sap.sse.util.ServiceTrackerFactory;
26 28
public class Activator implements BundleActivator {
27 29
private static Activator INSTANCE;
28 30
private ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> igtimiConnectionFactoryTracker;
29
- private ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
31
+ private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
30 32
31 33
public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
32 34
}
... ...
@@ -34,11 +36,11 @@ public class Activator implements BundleActivator {
34 36
@Override
35 37
public void start(final BundleContext context) throws Exception {
36 38
INSTANCE = this;
37
-
38 39
igtimiConnectionFactoryTracker = ServiceTrackerFactory
39 40
.createAndOpen(context, IgtimiConnectionFactory.class);
40
-
41
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
41
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
42
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
43
+ securityServiceTracker.open();
42 44
}
43 45
44 46
public static Activator getInstance() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
... ...
@@ -53,7 +55,11 @@ public class Activator implements BundleActivator {
53 55
}
54 56
55 57
public SecurityService getSecurityService() {
56
- return securityServiceTracker.getService();
58
+ try {
59
+ return securityServiceTracker.getInitializedService(0);
60
+ } catch (InterruptedException e) {
61
+ throw new RuntimeException(e);
62
+ }
57 63
}
58 64
59 65
@Override
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java
... ...
@@ -13,7 +13,6 @@ import org.apache.http.client.ClientProtocolException;
13 13
import org.json.simple.parser.ParseException;
14 14
import org.osgi.framework.BundleActivator;
15 15
import org.osgi.framework.BundleContext;
16
-import org.osgi.util.tracker.ServiceTracker;
17 16
18 17
import com.sap.sailing.domain.common.security.SecuredDomainType;
19 18
import com.sap.sailing.domain.igtimiadapter.Account;
... ...
@@ -23,6 +22,8 @@ import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
23 22
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
24 23
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
25 24
import com.sap.sailing.domain.tracking.WindTrackerFactory;
25
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
26
+import com.sap.sse.replication.ReplicationService;
26 27
import com.sap.sse.security.SecurityService;
27 28
import com.sap.sse.util.ClearStateTestSupport;
28 29
import com.sap.sse.util.ServiceTrackerFactory;
... ...
@@ -55,7 +56,7 @@ public class Activator implements BundleActivator {
55 56
private static final String CLIENT_REDIRECT_PORT_PROPERTY_NAME = "igtimi.client.redirect.port";
56 57
private final Future<IgtimiConnectionFactoryImpl> connectionFactory;
57 58
private final Future<IgtimiWindTrackerFactory> windTrackerFactory;
58
- private ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker;
59
+ private FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker;
59 60
private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true));
60 61
private SecurityService securityServiceTest;
61 62
... ...
@@ -106,10 +107,12 @@ public class Activator implements BundleActivator {
106 107
}
107 108
}
108 109
});
109
- securityServiceServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
110
+ securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
111
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
112
+ securityServiceServiceTracker.open();
110 113
new Thread(() -> {
111 114
try {
112
- final SecurityService securityService = securityServiceServiceTracker.waitForService(0);
115
+ final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0);
113 116
IgtimiConnectionFactoryImpl igtimiConnectionFactory = connectionFactory.get();
114 117
for (Account account : igtimiConnectionFactory.getAllAccounts()) {
115 118
securityService.migrateOwnership(account);
... ...
@@ -144,7 +147,11 @@ public class Activator implements BundleActivator {
144 147
}
145 148
146 149
public SecurityService getSecurityService() {
147
- return securityServiceTest == null ? securityServiceServiceTracker.getService() : securityServiceTest;
150
+ try {
151
+ return securityServiceTest == null ? securityServiceServiceTracker.getInitializedService(0) : securityServiceTest;
152
+ } catch (InterruptedException e) {
153
+ throw new RuntimeException(e);
154
+ }
148 155
}
149 156
150 157
public IgtimiWindTrackerFactory getWindTrackerFactory() {
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java
... ...
@@ -36,14 +36,16 @@ import com.sap.sailing.server.interfaces.RacingEventService;
36 36
import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler;
37 37
import com.sap.sse.MasterDataImportClassLoaderService;
38 38
import com.sap.sse.common.TypeBasedServiceFinder;
39
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
39 40
import com.sap.sse.replication.Replicable;
41
+import com.sap.sse.replication.ReplicationService;
40 42
import com.sap.sse.util.ServiceTrackerFactory;
41 43
42 44
public class Activator implements BundleActivator {
43 45
private static final Logger logger = Logger.getLogger(Activator.class.getName());
44 46
45 47
private static BundleContext context;
46
- private ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
48
+ private FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
47 49
private ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> sensorFixMapperTracker;
48 50
49 51
public static BundleContext getContext() {
... ...
@@ -84,13 +86,18 @@ public class Activator implements BundleActivator {
84 86
registrations.add(context.registerService(SensorFixMapper.class, new BravoExtendedDataFixMapper(), null));
85 87
registrations.add(context.registerService(SensorFixMapper.class, new ExpeditionExtendedDataFixMapper(), null));
86 88
sensorFixMapperTracker = createSensorFixMapperServiceTracker(context);
87
- racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
89
+ racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class,
90
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
91
+ racingEventServiceTracker.open();
88 92
RegattaLogFixTrackerRegattaListener regattaLogSensorDataTrackerTrackedRegattaListener = new RegattaLogFixTrackerRegattaListener(
89 93
racingEventServiceTracker, new SensorFixMapperFactoryImpl(sensorFixMapperTracker));
90 94
registrations.add(context.registerService(TrackedRegattaListener.class,
91 95
regattaLogSensorDataTrackerTrackedRegattaListener, null));
96
+ final Dictionary<String, String> replicableServiceProperties = new Hashtable<>();
97
+ replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name,
98
+ regattaLogSensorDataTrackerTrackedRegattaListener.getId().toString());
92 99
registrations.add(context.registerService(Replicable.class,
93
- regattaLogSensorDataTrackerTrackedRegattaListener, null));
100
+ regattaLogSensorDataTrackerTrackedRegattaListener, replicableServiceProperties));
94 101
new Thread(()->{
95 102
try {
96 103
registrations.add(context.registerService(RaceTrackingConnectivityParametersHandler.class,
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java
... ...
@@ -12,8 +12,6 @@ import java.util.Set;
12 12
import java.util.concurrent.ConcurrentHashMap;
13 13
import java.util.logging.Logger;
14 14
15
-import org.osgi.util.tracker.ServiceTracker;
16
-
17 15
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
18 16
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
19 17
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
... ...
@@ -24,14 +22,15 @@ import com.sap.sailing.domain.tracking.RaceTracker;
24 22
import com.sap.sailing.domain.tracking.TrackedRegatta;
25 23
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
26 24
import com.sap.sailing.server.interfaces.RacingEventService;
25
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
27 26
import com.sap.sse.replication.OperationExecutionListener;
28 27
import com.sap.sse.replication.OperationWithResult;
29 28
import com.sap.sse.replication.OperationWithResultWithIdWrapper;
30 29
import com.sap.sse.replication.OperationsToMasterSender;
30
+import com.sap.sse.replication.OperationsToMasterSendingQueue;
31 31
import com.sap.sse.replication.ReplicableWithObjectInputStream;
32 32
import com.sap.sse.replication.ReplicationMasterDescriptor;
33 33
import com.sap.sse.replication.ReplicationService;
34
-import com.sap.sse.replication.OperationsToMasterSendingQueue;
35 34
36 35
/**
37 36
* This is the main entry point of the {@link SensorFixStore} based fix tracking.
... ...
@@ -51,7 +50,7 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA
51 50
private static final Logger log = Logger.getLogger(RegattaLogFixTrackerRegattaListener.class.getName());
52 51
53 52
private final Map<RegattaAndRaceIdentifier, RaceLogFixTrackerManager> dataTrackers = new ConcurrentHashMap<>();
54
- private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
53
+ private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
55 54
private final SensorFixMapperFactory sensorFixMapperFactory;
56 55
57 56
/**
... ...
@@ -63,10 +62,11 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA
63 62
private OperationsToMasterSendingQueue unsentOperationsToMasterSender;
64 63
65 64
public RegattaLogFixTrackerRegattaListener(
66
- ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker,
65
+ FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker,
67 66
SensorFixMapperFactory sensorFixMapperFactory) {
68 67
this.racingEventServiceTracker = racingEventServiceTracker;
69 68
this.sensorFixMapperFactory = sensorFixMapperFactory;
69
+ this.currentlyFillingFromInitialLoad = false;
70 70
}
71 71
72 72
@Override
... ...
@@ -81,30 +81,38 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA
81 81
@Override
82 82
protected void onRaceAdded(RegattaAndRaceIdentifier raceIdentifier, DynamicTrackedRegatta trackedRegatta,
83 83
DynamicTrackedRace trackedRace) {
84
- racingEventServiceTracker.getService().getRaceTrackerByRegattaAndRaceIdentifier(raceIdentifier, (raceTracker) -> {
85
- if (raceTracker != null) {
86
- boolean added = raceTracker.add(new RaceTracker.Listener() {
87
- @Override
88
- public void onTrackerWillStop(boolean preemptive, boolean willBeRemoved) {
89
- raceTracker.remove(this);
90
- removeRaceLogSensorDataTracker(raceIdentifier, preemptive, willBeRemoved);
84
+ try {
85
+ racingEventServiceTracker.getInitializedService(0).getRaceTrackerByRegattaAndRaceIdentifier(raceIdentifier, (raceTracker) -> {
86
+ try {
87
+ if (raceTracker != null) {
88
+ boolean added = raceTracker.add(new RaceTracker.Listener() {
89
+ @Override
90
+ public void onTrackerWillStop(boolean preemptive, boolean willBeRemoved) {
91
+ raceTracker.remove(this);
92
+ removeRaceLogSensorDataTracker(raceIdentifier, preemptive, willBeRemoved);
93
+ }
94
+ });
95
+ // if !added, the RaceTracker is already stopped, so we are not allowed to start fix tracking
96
+ if (added) {
97
+ RaceLogFixTrackerManager trackerManager = new RaceLogFixTrackerManager(
98
+ (DynamicTrackedRace) trackedRace, racingEventServiceTracker.getInitializedService(0).getSensorFixStore(),
99
+ sensorFixMapperFactory);
100
+ RaceLogFixTrackerManager oldInstance = null;
101
+ synchronized (this) {
102
+ oldInstance = dataTrackers.put(raceIdentifier, trackerManager);
103
+ }
104
+ if (oldInstance != null) {
105
+ oldInstance.stop(/* preemptive */ true, /* willBeRemoved */ false);
106
+ }
107
+ }
108
+ }
109
+ } catch (InterruptedException e) {
110
+ throw new RuntimeException(e);
91 111
}
92 112
});
93
- // if !added, the RaceTracker is already stopped, so we are not allowed to start fix tracking
94
- if (added) {
95
- RaceLogFixTrackerManager trackerManager = new RaceLogFixTrackerManager(
96
- (DynamicTrackedRace) trackedRace, racingEventServiceTracker.getService().getSensorFixStore(),
97
- sensorFixMapperFactory);
98
- RaceLogFixTrackerManager oldInstance = null;
99
- synchronized (this) {
100
- oldInstance = dataTrackers.put(raceIdentifier, trackerManager);
101
- }
102
- if (oldInstance != null) {
103
- oldInstance.stop(/* preemptive */ true, /* willBeRemoved */ false);
104
- }
105
- }
113
+ } catch (InterruptedException e) {
114
+ throw new RuntimeException(e);
106 115
}
107
- });
108 116
}
109 117
110 118
@Override
... ...
@@ -130,7 +138,7 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA
130 138
131 139
// Replication related methods and fields
132 140
private final ConcurrentHashMap<OperationExecutionListener<RegattaLogFixTrackerRegattaListener>, OperationExecutionListener<RegattaLogFixTrackerRegattaListener>> operationExecutionListeners = new ConcurrentHashMap<>();
133
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
141
+ private volatile boolean currentlyFillingFromInitialLoad;
134 142
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
135 143
private final Set<OperationWithResultWithIdWrapper<RegattaLogFixTrackerRegattaListener, ?>> operationsSentToMasterForReplication = new HashSet<>();
136 144
private ReplicationMasterDescriptor master;
... ...
@@ -174,12 +182,12 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA
174 182
175 183
@Override
176 184
public boolean isCurrentlyFillingFromInitialLoad() {
177
- return currentlyFillingFromInitialLoad.get();
185
+ return currentlyFillingFromInitialLoad;
178 186
}
179 187
180 188
@Override
181 189
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
182
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
190
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
183 191
}
184 192
185 193
@Override
java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF
... ...
@@ -14,7 +14,8 @@ Require-Bundle: com.sap.sailing.domain.swisstimingadapter,
14 14
com.sap.sailing.domain.persistence,
15 15
com.sap.sse,
16 16
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
17
- com.sap.sse.security.common
17
+ com.sap.sse.security.common,
18
+ com.sap.sse.replication.interfaces
18 19
Export-Package: com.sap.sailing.domain.swisstimingadapter.persistence,
19 20
com.sap.sailing.domain.swisstimingadapter.persistence.impl;x-friends:="com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.mongodb.test"
20 21
Import-Package: com.sap.sse.security,
java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java
... ...
@@ -22,6 +22,8 @@ import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterP
22 22
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
23 23
import com.sap.sse.common.TypeBasedServiceFinder;
24 24
import com.sap.sse.mongodb.MongoDBService;
25
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
26
+import com.sap.sse.replication.ReplicationService;
25 27
import com.sap.sse.security.SecurityService;
26 28
import com.sap.sse.util.ServiceTrackerFactory;
27 29
... ...
@@ -34,8 +36,9 @@ public class Activator implements BundleActivator {
34 36
MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
35 37
}
36 38
new Thread(() -> {
37
- final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory
38
- .createAndOpen(context, SecurityService.class);
39
+ final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
40
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
41
+ securityServiceServiceTracker.open();
39 42
final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class);
40 43
final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class);
41 44
final ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> swissTimingAdapterFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class);
... ...
@@ -43,7 +46,7 @@ public class Activator implements BundleActivator {
43 46
final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0);
44 47
final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0);
45 48
final SwissTimingAdapterFactory swissTimingAdapterFactory = swissTimingAdapterFactoryServiceTracker.waitForService(0);
46
- final SecurityService securityService = securityServiceServiceTracker.waitForService(0);
49
+ final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0);
47 50
final Dictionary<String, Object> properties = new Hashtable<String, Object>();
48 51
final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = swissTimingAdapterFactory.getOrCreateSwissTimingAdapter(
49 52
domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory();
... ...
@@ -53,21 +56,17 @@ public class Activator implements BundleActivator {
53 56
domainFactory);
54 57
properties.put(TypeBasedServiceFinder.TYPE, SwissTimingTrackingConnectivityParameters.TYPE);
55 58
context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties);
56
-
57 59
for (SwissTimingArchiveConfiguration swissTimingArchive : SwissTimingAdapterPersistence.INSTANCE
58 60
.getSwissTimingArchiveConfigurations()) {
59 61
securityService.migrateOwnership(swissTimingArchive);
60 62
}
61
-
62 63
for (SwissTimingConfiguration swissTiming : SwissTimingAdapterPersistence.INSTANCE
63 64
.getSwissTimingConfigurations()) {
64 65
securityService.migrateOwnership(swissTiming);
65 66
}
66
-
67 67
// we do not necessarily have swisstiming configs, so ensure that migration is marked as done
68 68
securityService.assumeOwnershipMigrated(SecuredDomainType.SWISS_TIMING_ACCOUNT.getName());
69 69
securityService.assumeOwnershipMigrated(SecuredDomainType.SWISS_TIMING_ARCHIVE_ACCOUNT.getName());
70
-
71 70
} catch (Exception e) {
72 71
logger.log(Level.SEVERE, "Exception trying to register SwissTiming RaceTrackingConnectivityParametersHandler implementation", e);
73 72
}
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java
... ...
@@ -14,7 +14,6 @@ import java.util.function.Consumer;
14 14
import org.junit.Rule;
15 15
import org.junit.Test;
16 16
import org.junit.rules.Timeout;
17
-import org.osgi.util.tracker.ServiceTracker;
18 17
19 18
import com.sap.sailing.domain.base.Course;
20 19
import com.sap.sailing.domain.base.RaceDefinition;
... ...
@@ -35,6 +34,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
35 34
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
36 35
import com.sap.sailing.server.impl.RacingEventServiceImpl;
37 36
import com.sap.sailing.server.interfaces.RacingEventService;
37
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
38 38
import com.sap.sse.util.ThreadLocalTransporter;
39 39
40 40
public class TestDeadlockInRegattaListener {
... ...
@@ -46,8 +46,8 @@ public class TestDeadlockInRegattaListener {
46 46
CyclicBarrier latch = new CyclicBarrier(2);
47 47
CyclicBarrier monitorOnRegattaListenerLatch = new CyclicBarrier(2);
48 48
@SuppressWarnings("unchecked")
49
- ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker =
50
- (ServiceTracker<RacingEventService, RacingEventService>) mock(ServiceTracker.class);
49
+ FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker =
50
+ (FullyInitializedReplicableTracker<RacingEventService>) mock(FullyInitializedReplicableTracker.class);
51 51
final String regattaName = "Test Regatta";
52 52
final RegattaImpl regatta = new RegattaImpl(
53 53
/* raceLogStore */ null, EmptyRegattaLogStore.INSTANCE, regattaName,
... ...
@@ -102,7 +102,7 @@ public class TestDeadlockInRegattaListener {
102 102
}
103 103
}
104 104
};
105
- when(racingEventServiceTracker.getService()).thenReturn(racingEventService);
105
+ when(racingEventServiceTracker.getInitializedService(0)).thenReturn(racingEventService);
106 106
RegattaLogFixTrackerRegattaListener listener = new RegattaLogFixTrackerRegattaListener(
107 107
racingEventServiceTracker, null);
108 108
java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF
... ...
@@ -14,7 +14,8 @@ Require-Bundle: com.sap.sse.mongodb,
14 14
com.sap.sailing.domain.persistence,
15 15
com.sap.sse,
16 16
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
17
- com.sap.sse.security.common
17
+ com.sap.sse.security.common,
18
+ com.sap.sse.replication.interfaces
18 19
Export-Package: com.sap.sailing.domain.tractracadapter.persistence,
19 20
com.sap.sailing.domain.tractracadapter.persistence.impl;x-friends:="com.sap.sailing.mongodb.test"
20 21
Import-Package: com.sap.sse.security,
java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java
... ...
@@ -23,18 +23,20 @@ import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
23 23
import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl;
24 24
import com.sap.sse.common.TypeBasedServiceFinder;
25 25
import com.sap.sse.mongodb.MongoDBService;
26
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
27
+import com.sap.sse.replication.ReplicationService;
26 28
import com.sap.sse.security.SecurityService;
27 29
import com.sap.sse.util.ClearStateTestSupport;
28 30
import com.sap.sse.util.ServiceTrackerFactory;
29 31
30 32
public class Activator implements BundleActivator {
31 33
private static final Logger logger = Logger.getLogger(Activator.class.getName());
32
-
34
+
33 35
/**
34 36
* Requires a base {@link DomainFactory} as well as a {@link RaceLogStore} and a {@link RegattaLogStore}. In order
35
- * to instantiate those two stores, the method requires a {@link MongoObjectFactory} and a {@link DomainObjectFactory}
36
- * which are waited for as services from the OSGi registry. This happens in a separate thread that the {@link #start(BundleContext)}
37
- * method launches.
37
+ * to instantiate those two stores, the method requires a {@link MongoObjectFactory} and a
38
+ * {@link DomainObjectFactory} which are waited for as services from the OSGi registry. This happens in a separate
39
+ * thread that the {@link #start(BundleContext)} method launches.
38 40
*/
39 41
@Override
40 42
public void start(BundleContext context) throws Exception {
... ...
@@ -42,22 +44,29 @@ public class Activator implements BundleActivator {
42 44
MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name());
43 45
}
44 46
new Thread(() -> {
45
- final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class);
46
- final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class);
47
- final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory
48
- .createAndOpen(context, SecurityService.class);
49
- final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterFactoryTracker = ServiceTrackerFactory.createAndOpen(context, TracTracAdapterFactory.class);
47
+ final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory
48
+ .createAndOpen(context, MongoObjectFactory.class);
49
+ final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory
50
+ .createAndOpen(context, DomainObjectFactory.class);
51
+ final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(
52
+ context, SecurityService.class, /* customizer */ null,
53
+ ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
54
+ securityServiceServiceTracker.open();
55
+ final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterFactoryTracker = ServiceTrackerFactory
56
+ .createAndOpen(context, TracTracAdapterFactory.class);
50 57
try {
51 58
final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0);
52 59
final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0);
53
- final SecurityService securityService = securityServiceServiceTracker.waitForService(0);
60
+ final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0);
54 61
final TracTracAdapterFactory tractracAdapterFactory = tractracAdapterFactoryTracker.waitForService(0);
55 62
final Dictionary<String, Object> properties = new Hashtable<String, Object>();
56
- final com.sap.sailing.domain.tractracadapter.DomainFactory domainFactory = tractracAdapterFactory.getOrCreateTracTracAdapter(
57
- domainObjectFactory.getBaseDomainFactory()).getTracTracDomainFactory();
63
+ final com.sap.sailing.domain.tractracadapter.DomainFactory domainFactory = tractracAdapterFactory
64
+ .getOrCreateTracTracAdapter(domainObjectFactory.getBaseDomainFactory())
65
+ .getTracTracDomainFactory();
58 66
final TracTracConnectivityParamsHandler paramsHandler = new TracTracConnectivityParamsHandler(
59 67
MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory),
60
- MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory),
68
+ MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory,
69
+ domainObjectFactory),
61 70
domainFactory);
62 71
63 72
com.sap.sailing.domain.tractracadapter.persistence.DomainObjectFactory tractracDomainObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory.INSTANCE
... ...
@@ -79,7 +88,9 @@ public class Activator implements BundleActivator {
79 88
}
80 89
}, null);
81 90
} catch (Exception e) {
82
- logger.log(Level.SEVERE, "Exception trying to register TracTrac RaceTrackingConnectivityParametersHandler implementation", e);
91
+ logger.log(Level.SEVERE,
92
+ "Exception trying to register TracTrac RaceTrackingConnectivityParametersHandler implementation",
93
+ e);
83 94
}
84 95
}, getClass().getName() + " registering connectivity handler").start();
85 96
}
java/com.sap.sailing.domain/META-INF/MANIFEST.MF
... ...
@@ -30,7 +30,6 @@ Export-Package: com.sap.sailing.domain.base,
30 30
com.sap.sailing.domain.regattalog,
31 31
com.sap.sailing.domain.regattalog.impl,
32 32
com.sap.sailing.domain.sharding,
33
- com.sap.sailing.domain.sharedsailingdata,
34 33
com.sap.sailing.domain.trackfiles,
35 34
com.sap.sailing.domain.trackimport,
36 35
com.sap.sailing.domain.tracking,
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java
... ...
@@ -986,24 +986,32 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
986 986
}
987 987
final Speed averageSpeedOverGround = trackedLeg.getAverageSpeedOverGround(timePoint);
988 988
result.averageSpeedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots();
989
- Distance averageAbsoluteCrossTrackError;
989
+ final boolean hasFinishedLeg = trackedLeg.hasFinishedLeg(timePoint);
990
+ Distance currentOrAverageAbsoluteCrossTrackError;
990 991
try {
991
- averageAbsoluteCrossTrackError = trackedLeg.getAverageAbsoluteCrossTrackError(timePoint, waitForLatestAnalyses);
992
+ if (hasFinishedLeg) {
993
+ currentOrAverageAbsoluteCrossTrackError = trackedLeg.getAverageAbsoluteCrossTrackError(timePoint, waitForLatestAnalyses);
994
+ } else {
995
+ currentOrAverageAbsoluteCrossTrackError = trackedLeg.getAbsoluteCrossTrackError(timePoint);
996
+ }
992 997
} catch (NoWindException nwe) {
993 998
// leave averageAbsoluteCrossTrackError as null, meaning "unknown"
994
- averageAbsoluteCrossTrackError = null;
999
+ currentOrAverageAbsoluteCrossTrackError = null;
995 1000
}
996
- result.averageAbsoluteCrossTrackErrorInMeters = averageAbsoluteCrossTrackError == null ? null : averageAbsoluteCrossTrackError.getMeters();
997
- Distance averageSignedCrossTrackError;
1001
+ result.currentOrAverageAbsoluteCrossTrackErrorInMeters = currentOrAverageAbsoluteCrossTrackError == null ? null : currentOrAverageAbsoluteCrossTrackError.getMeters();
1002
+ Distance currentOrAverageSignedCrossTrackError;
998 1003
try {
999
- averageSignedCrossTrackError = trackedLeg.getAverageSignedCrossTrackError(timePoint, waitForLatestAnalyses);
1004
+ if (hasFinishedLeg) {
1005
+ currentOrAverageSignedCrossTrackError = trackedLeg.getAverageSignedCrossTrackError(timePoint, waitForLatestAnalyses);
1006
+ } else {
1007
+ currentOrAverageSignedCrossTrackError = trackedLeg.getSignedCrossTrackError(timePoint);
1008
+ }
1000 1009
} catch (NoWindException nwe) {
1001 1010
// leave averageSignedCrossTrackError as null, meaning "unknown"
1002
- averageSignedCrossTrackError = null;
1011
+ currentOrAverageSignedCrossTrackError = null;
1003 1012
}
1004
- result.averageSignedCrossTrackErrorInMeters = averageSignedCrossTrackError == null ? null : averageSignedCrossTrackError.getMeters();
1013
+ result.currentOrAverageSignedCrossTrackErrorInMeters = currentOrAverageSignedCrossTrackError == null ? null : currentOrAverageSignedCrossTrackError.getMeters();
1005 1014
Double speedOverGroundInKnots;
1006
- final boolean hasFinishedLeg = trackedLeg.hasFinishedLeg(timePoint);
1007 1015
if (hasFinishedLeg) {
1008 1016
speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots();
1009 1017
final Distance averageRideHeight = trackedLeg.getAverageRideHeight(timePoint);
... ...
@@ -1039,7 +1047,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
1039 1047
// calls. To avoid having to use expensive locking, we'll just double-check here if legFinishTime is null and
1040 1048
// treat this as if trackedLeg.hasFinishedLeg(timePoint) had returned false.
1041 1049
result.correctedTotalTime = trackedLeg.hasStartedLeg(timePoint) ? trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getCorrectedTime(trackedLeg.getCompetitor(),
1042
- trackedLeg.hasFinishedLeg(timePoint) && legFinishTime != null ? legFinishTime : timePoint, cache) : null;
1050
+ hasFinishedLeg && legFinishTime != null ? legFinishTime : timePoint, cache) : null;
1043 1051
// fetch the leg gap in own corrected time from the ranking metric
1044 1052
final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().
1045 1053
getLegGapToLegLeaderInOwnTime(trackedLeg, timePoint, rankingInfo, cache);
... ...
@@ -1058,7 +1066,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
1058 1066
}
1059 1067
result.started = trackedLeg.hasStartedLeg(timePoint);
1060 1068
Speed velocityMadeGood;
1061
- if (trackedLeg.hasFinishedLeg(timePoint)) {
1069
+ if (hasFinishedLeg) {
1062 1070
velocityMadeGood = trackedLeg.getAverageVelocityMadeGood(timePoint);
1063 1071
} else {
1064 1072
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharedsailingdata/SharedSailingData.java
... ...
@@ -1,140 +0,0 @@
1
-package com.sap.sailing.domain.sharedsailingdata;
2
-
3
-import java.net.URL;
4
-import java.util.ArrayList;
5
-import java.util.Map;
6
-import java.util.Optional;
7
-import java.util.UUID;
8
-
9
-import com.sap.sailing.domain.common.DeviceIdentifier;
10
-import com.sap.sailing.domain.common.Position;
11
-import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
12
-import com.sap.sailing.domain.coursetemplate.CourseTemplate;
13
-import com.sap.sailing.domain.coursetemplate.FixedPositioning;
14
-import com.sap.sailing.domain.coursetemplate.MarkProperties;
15
-import com.sap.sailing.domain.coursetemplate.MarkRole;
16
-import com.sap.sailing.domain.coursetemplate.MarkTemplate;
17
-import com.sap.sailing.domain.coursetemplate.Positioning;
18
-import com.sap.sailing.domain.coursetemplate.RepeatablePart;
19
-import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
20
-import com.sap.sse.common.TimePoint;
21
-import com.sap.sse.common.impl.MillisecondsTimePoint;
22
-import com.sap.sse.security.SecurityService;
23
-import com.sap.sse.security.shared.HasPermissions.DefaultActions;
24
-import com.sap.sse.security.shared.impl.UserGroup;
25
-
26
-/**
27
- * {@link com.sap.sailing.server.interfaces.RacingEventService RacingEventService} is scoped to one server only. To
28
- * share data that is not exclusively bound to one specific server but relevant for several servers,
29
- * {@link SharedSailingData} provides an alternative replication scope. This means {@link SharedSailingData} may be
30
- * replicated in a similar way as {@link SecurityService}.<br>
31
- * {@link SharedSailingData} encapsulates persistence, replication and security aspects for the following domain types:
32
- * <ul>
33
- * <li>{@link MarkTemplate}s</li>
34
- * <li>{@link MarkProperties}</li>
35
- * <li>{@link CourseTemplate}s</li>
36
- * </ul>
37
- *
38
- * In particular, the reading methods that produce iterables of entities, such as {@link #getAllMarkRoles()} or
39
- * {@link #getAllMarkTemplates()}, will deliver a view restricted by the availability of the {@link DefaultActions#READ}
40
- * permission of the user on whose behalf the request is being executed.
41
- *
42
- * @author Axel Uhl (D043530)
43
- *
44
- */
45
-public interface SharedSailingData {
46
-
47
- Iterable<MarkProperties> getAllMarkProperties(Iterable<String> tagsToFilterFor);
48
-
49
- Iterable<MarkTemplate> getAllMarkTemplates();
50
-
51
- Iterable<CourseTemplate> getAllCourseTemplates(Iterable<String> tagsToFilterFor);
52
-
53
- Iterable<MarkRole> getAllMarkRoles();
54
-
55
- MarkRole createMarkRole(String name, String shortName);
56
-
57
- MarkRole getMarkRoleById(UUID id);
58
-
59
- /**
60
- * @param optionalNonDefaultGroupOwnership
61
- * if {@link Optional#isPresent() present}, defines the {@link UserGroup} to use for the new mark
62
- * properties object's group ownership. Otherwise, the session user's default creation group ownership
63
- * will be used for the current server.
64
- */
65
- MarkProperties createMarkProperties(CommonMarkProperties properties, Iterable<String> tags, Optional<UserGroup> optionalNonDefaultGroupOwnership);
66
-
67
- MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Iterable<String> tags);
68
-
69
- /**
70
- * @param positioningInformation
71
- * if {@code null}, no update will be performed to the positioning information of the
72
- * {@link MarkProperties} object identified by {@code uuid}. To clear the positioning information, pass
73
- * in a valid, non-{@code null} {@link Positioning} object that makes an "empty" specification, such as a
74
- * {@link FixedPositioning} with a {@code null} {@link FixedPositioning#getFixedPosition()} return.
75
- */
76
- MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Positioning positioningInformation, Iterable<String> tags);
77
-
78
- /**
79
- * This overrides a previously set fixed position or associated tracking device.
80
- */
81
- void setFixedPositionForMarkProperties(MarkProperties markProperties, Position position);
82
-
83
- MarkProperties getMarkPropertiesById(UUID id);
84
-
85
- /**
86
- * This overrides a previously set fixed position or associated tracking device.
87
- */
88
- void setTrackingDeviceIdentifierForMarkProperties(MarkProperties markProperties, DeviceIdentifier deviceIdentifier);
89
-
90
- MarkTemplate createMarkTemplate(CommonMarkProperties properties);
91
-
92
- MarkTemplate getMarkTemplateById(UUID id);
93
-
94
- /**
95
- * @param waypoints the waypoints in their defined order (iteration order equals order of waypoints in course)
96
- */
97
- CourseTemplate createCourseTemplate(String courseTemplateName, String courseTemplateShortName,
98
- Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints,
99
- Map<MarkTemplate, MarkRole> associatedRoles, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles,
100
- RepeatablePart optionalRepeatablePart, Iterable<String> tags, URL optionalImageURL, Integer defaultNumberOfLaps);
101
-
102
- CourseTemplate getCourseTemplateById(UUID id);
103
-
104
- /**
105
- * Records the fact that the {@code markProperties} were used to configure a mark based on a
106
- * {@code markTemplate}. Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be
107
- * returned for {@code markTemplate} when invoking {@link #getUsedMarkProperties(MarkTemplate)}.
108
- */
109
- void recordUsage(MarkTemplate markTemplate, MarkProperties markProperties);
110
-
111
- /**
112
- * Records the fact that the {@code markProperties} were used to configure a mark that takes the given role name.
113
- * Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be returned for the role name
114
- * when invoking {@link #getUsedMarkProperties(String)}.
115
- */
116
- void recordUsage(MarkProperties markProperties, MarkRole markRole);
117
-
118
- /**
119
- * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkTemplate, MarkProperties) last used}
120
- * for the {@link MarkTemplate} passed in the {@code markTemplate} parameter.
121
- */
122
- Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkTemplate markTemplate);
123
-
124
- /**
125
- * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkProperties, String) last used}
126
- * for the role name passed in the {@code roleName} parameter.
127
- */
128
- Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkRole roleName);
129
-
130
- void deleteMarkProperties(MarkProperties markProperties);
131
-
132
- void deleteCourseTemplate(CourseTemplate courseTemplate);
133
-
134
- Iterable<MarkProperties> getAllMarkProperties();
135
-
136
- Iterable<CourseTemplate> getAllCourseTemplates();
137
-
138
- CourseTemplate updateCourseTemplate(UUID uuid, String name, String shortName, URL optionalImageURL, ArrayList<String> tags,
139
- Integer defaultNumberOfLaps);
140
-}
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java
... ...
@@ -289,6 +289,14 @@ public interface TrackedLegOfCompetitor extends Serializable {
289 289
* occurred then the time point of the mark passing of the leg end mark will be taken into account.
290 290
*/
291 291
Distance getAverageAbsoluteCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
292
+
293
+ /**
294
+ * Computes the current absolute cross-track error (positive sign or zero, regardless of whether the competitor
295
+ * is right or left of the course middle line of the current leg).
296
+ *
297
+ * @return {@code null} if the competitor has not started or already finished the leg
298
+ */
299
+ Distance getAbsoluteCrossTrackError(TimePoint timePoint) throws NoWindException;
292 300
293 301
/**
294 302
* Computes the average signed cross track error for this leg. The cross track error for each fix is taken to be a
... ...
@@ -299,6 +307,14 @@ public interface TrackedLegOfCompetitor extends Serializable {
299 307
*/
300 308
Distance getAverageSignedCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
301 309
310
+ /**
311
+ * Computes the current signed cross-track error (negative sign means left of the course middle line looking in the direction
312
+ * of the leg; positive sign means right of the course middle line).
313
+ *
314
+ * @return {@code null} if the competitor has not started or already finished the leg
315
+ */
316
+ Distance getSignedCrossTrackError(TimePoint timePoint) throws NoWindException;
317
+
302 318
TrackedLeg getTrackedLeg();
303 319
304 320
/**
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java
... ...
@@ -547,6 +547,16 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
547 547
}
548 548
549 549
@Override
550
+ public Distance getAbsoluteCrossTrackError(TimePoint timePoint) throws NoWindException {
551
+ return getTrackedLeg().getAbsoluteCrossTrackError(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */ true), timePoint);
552
+ }
553
+
554
+ @Override
555
+ public Distance getSignedCrossTrackError(TimePoint timePoint) throws NoWindException {
556
+ return getTrackedLeg().getSignedCrossTrackError(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */ true), timePoint);
557
+ }
558
+
559
+ @Override
550 560
public Duration getGapToLeader(TimePoint timePoint, final Competitor leaderInLegAtTimePoint,
551 561
final RankingInfo rankingInfo, WindPositionMode windPositionMode) throws NoWindException {
552 562
return getGapToLeader(timePoint, leaderInLegAtTimePoint, windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint));
java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java
... ...
@@ -12,7 +12,6 @@ import java.util.logging.Logger;
12 12
import org.osgi.framework.BundleActivator;
13 13
import org.osgi.framework.BundleContext;
14 14
import org.osgi.framework.ServiceRegistration;
15
-import org.osgi.util.tracker.ServiceTracker;
16 15
17 16
import com.sap.sailing.domain.common.security.SecuredDomainType;
18 17
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
... ...
@@ -28,6 +27,8 @@ import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory;
28 27
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
29 28
import com.sap.sse.ServerInfo;
30 29
import com.sap.sse.common.TypeBasedServiceFinder;
30
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
31
+import com.sap.sse.replication.ReplicationService;
31 32
import com.sap.sse.security.SecurityService;
32 33
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
33 34
import com.sap.sse.security.shared.impl.WildcardPermissionEncoder;
... ...
@@ -91,10 +92,11 @@ public class Activator implements BundleActivator {
91 92
registrations.add(context.registerService(DeviceIdentifierStringSerializationHandler.class, new ExpeditionSensorStringSerializationHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE)));
92 93
93 94
new Thread(() -> {
94
- final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory
95
- .createAndOpen(context, SecurityService.class);
95
+ final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(
96
+ context, SecurityService.class, /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
97
+ securityServiceServiceTracker.open();
96 98
try {
97
- final SecurityService securityService = securityServiceServiceTracker.waitForService(0);
99
+ final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0);
98 100
final WildcardPermissionEncoder permissionEncoder = new WildcardPermissionEncoder();
99 101
for (ExpeditionDeviceConfiguration deviceConfiguration : expeditionTrackerFactory
100 102
.getDeviceConfigurations()) {
java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch
... ...
@@ -116,7 +116,7 @@
116 116
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/>
117 117
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
118 118
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
119
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -bindAddress 0.0.0.0 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/>
119
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/>
120 120
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
121 121
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:+UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
122 122
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/>
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
... ...
@@ -9,9 +9,15 @@
9 9
<li>The security infrastructure of the previously separate environments of
10 10
www.sapsailing.com, dedicated event servers and other sub-domains such as
11 11
club-level or sailing federation servers with their
12
- own sub-domains has now been merged. Users can now freely roam from specific
13
- servers to www.sapsailing.com and back without losing their user session,
14
- preferences or sailor profiles. One sapsailing.com account does it all.</li>
12
+ own sub-domains has now been merged. Users can now roam freely from specific
13
+ servers to <a href="https://www.sapsailing.com">www.sapsailing.com</a> and back without losing their user session,
14
+ preferences or sailor profiles. One <tt>sapsailing.com</tt> account does it all.</li>
15
+ <li>The cross track error values (XTE) for the legs now contain the actual values at the time point for which the
16
+ leaderboard is shown when the competitor is sailing in that leg at this time point; after the leg, as in previous
17
+ versions, the average values are shown. With this, during a leg you can sort by the current distance to the
18
+ leg's course middle line, absolute or signed. Note that this change has no effect on the <em>race-level</em>
19
+ cross track error values which are still computed as averages at all times, restricted to the upwind legs of the
20
+ race.</li>
15 21
</ul>
16 22
17 23
<h5 class="articleSubheadline">January 2020</h5>
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java
18 24
index 8b195ce..9dfff8d
19
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java
25
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java
... ...
@@ -23,35 +23,47 @@ import com.sap.sse.gwt.dispatch.client.transport.gwtrpc.RequestWrapper;
23 23
import com.sap.sse.gwt.dispatch.servlets.AbstractDispatchServlet;
24 24
import com.sap.sse.gwt.dispatch.shared.commands.Action;
25 25
import com.sap.sse.gwt.dispatch.shared.commands.Result;
26
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
27
+import com.sap.sse.replication.ReplicationService;
26 28
import com.sap.sse.security.SecurityService;
27 29
import com.sap.sse.util.ServiceTrackerFactory;
28 30
29 31
public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispatchContext> {
30 32
private static final long serialVersionUID = -245230476512348999L;
31 33
32
- private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
34
+ private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
33 35
private final ServiceTracker<WindFinderTrackerFactory, WindFinderTrackerFactory> windFinderTrackerFactory;
34 36
private final ServiceTracker<EventNewsService, EventNewsService> eventNewsServiceTracker;
35
- private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
37
+ private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
36 38
private final ServiceTracker<TrackedRaceStatisticsCache, TrackedRaceStatisticsCache> trackedRaceStatisticsCacheTracker;
37 39
38 40
public SailingDispatchServlet() {
39 41
final BundleContext context = Activator.getDefault();
40
- racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
42
+ final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker =
43
+ ServiceTrackerFactory.createAndOpen(context, ReplicationService.class);
44
+ racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class,
45
+ /* customizer */ null, replicationServiceTracker);
46
+ racingEventServiceTracker.open();
41 47
windFinderTrackerFactory = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class);
42 48
eventNewsServiceTracker = ServiceTrackerFactory.createAndOpen(context, EventNewsService.class);
43
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
49
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
50
+ /* customizer */ null, replicationServiceTracker);
51
+ securityServiceTracker.open();
44 52
trackedRaceStatisticsCacheTracker = ServiceTrackerFactory.createAndOpen(context, TrackedRaceStatisticsCache.class);
45 53
}
46 54
47 55
@Override
48 56
protected <R extends Result, A extends Action<R, SailingDispatchContext>> SailingDispatchContext createDispatchContextFor(
49 57
RequestWrapper<R, A, SailingDispatchContext> request) {
50
- return new SailingDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getService(),
51
- windFinderTrackerFactory.getService(),
52
- eventNewsServiceTracker.getService(), securityServiceTracker.getService(),
53
- trackedRaceStatisticsCacheTracker.getService(),
54
- request.getClientLocaleName(), getThreadLocalRequest());
58
+ try {
59
+ return new SailingDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getInitializedService(0),
60
+ windFinderTrackerFactory.getService(),
61
+ eventNewsServiceTracker.getService(), securityServiceTracker.getInitializedService(0),
62
+ trackedRaceStatisticsCacheTracker.getService(),
63
+ request.getClientLocaleName(), getThreadLocalRequest());
64
+ } catch (InterruptedException e) {
65
+ throw new RuntimeException(e);
66
+ }
55 67
}
56 68
57 69
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/ServerConfigurationServiceImpl.java
... ...
@@ -1,12 +1,13 @@
1 1
package com.sap.sailing.gwt.server;
2 2
3 3
import org.osgi.framework.BundleContext;
4
-import org.osgi.util.tracker.ServiceTracker;
5 4
6 5
import com.sap.sailing.gwt.ui.client.ServerConfigurationService;
7 6
import com.sap.sailing.gwt.ui.server.Activator;
8 7
import com.sap.sailing.server.interfaces.RacingEventService;
9 8
import com.sap.sse.gwt.server.ProxiedRemoteServiceServlet;
9
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
10
+import com.sap.sse.replication.ReplicationService;
10 11
import com.sap.sse.util.ServiceTrackerFactory;
11 12
12 13
/**
... ...
@@ -15,15 +16,21 @@ import com.sap.sse.util.ServiceTrackerFactory;
15 16
public class ServerConfigurationServiceImpl extends ProxiedRemoteServiceServlet implements ServerConfigurationService {
16 17
private static final long serialVersionUID = 2571063542941630865L;
17 18
18
- private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
19
+ private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
19 20
20 21
public ServerConfigurationServiceImpl() {
21 22
BundleContext context = Activator.getDefault();
22
- racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
23
+ racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class,
24
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
25
+ racingEventServiceTracker.open();
23 26
}
24 27
25 28
protected RacingEventService getService() {
26
- return racingEventServiceTracker.getService();
29
+ try {
30
+ return racingEventServiceTracker.getInitializedService(0);
31
+ } catch (InterruptedException e) {
32
+ throw new RuntimeException(e);
33
+ }
27 34
}
28 35
29 36
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -367,9 +367,9 @@ leaderboardsInGroup=Leaderboards in group
367 367
replication=Replication
368 368
errorFetchingReplicaData=Error fetching replica data: {0}
369 369
averageAbsoluteCrossTrackErrorInMeters=\u2205 XTE
370
-averageAbsoluteCrossTrackErrorInMetersTooltip=The average cross track error to the great circle segment connecting leg start with leg end
370
+averageAbsoluteCrossTrackErrorInMetersTooltip=The average cross track error to the great circle segment connecting leg start with leg end; shows the average after the leg, and the current value while in the leg.
371 371
averageSignedCrossTrackErrorInMeters=\u2205 XTE +/-
372
-averageSignedCrossTrackErrorInMetersTooltip=The average position on the course as a signed distance from the course middle line; 0 meaning on the course middle line; negative values mean left-hand side, positive values mean right-hand side.
372
+averageSignedCrossTrackErrorInMetersTooltip=The average position on the course as a signed distance from the course middle line; 0 meaning on the course middle line; negative values mean left-hand side, positive values mean right-hand side; shows the average after the leg, and the current value while in the leg.
373 373
raceAverageAbsoluteCrossTrackErrorInMetersTooltip=The average absolute cross track error to the great circle segments connecting leg start with leg end for all upwind legs of this race, ignoring to which side of the course middle line the competitor is
374 374
raceAverageSignedCrossTrackErrorInMetersTooltip=The signed average cross track error to the great circle segments connecting leg start with leg end for all upwind legs of this race; negative values mean left of the course middle line, positive values mean right.
375 375
enterMaster=Hostname of Master Instance
... ...
@@ -2246,7 +2246,7 @@ migrateCompetitors=Migrate competitors
2246 2246
migrateBoats=Migrate boats
2247 2247
migrateHierarchyToGroupOwner=Migrate hierarchy to group owner
2248 2248
serverIsPublicButTenantIsNot=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration. Change your default tenant via the user profile if you want a default public server scenario.
2249
-serverIsPublicButTenantIsNotAndCouldBeChanged=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration.Should we change the default tenant for you?
2249
+serverIsPublicButTenantIsNotAndCouldBeChanged=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration. Should we change the default tenant for you?
2250 2250
copyMembersAndRoles=Copy members and roles
2251 2251
invalidSecret=Invalid Secret
2252 2252
warningSailInsightVersion=The server is configured for use with SAP Sail Insight 1. Open regattas are only supported in SAP Sail Insight 2. This can cause problems in use
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
... ...
@@ -365,8 +365,8 @@ leaderboardsInGroup=Ranglisten in der Gruppe
365 365
replication=Replikation
366 366
errorFetchingReplicaData=Fehler beim Beschaffen der Replikationsdaten: {0}
367 367
averageAbsoluteCrossTrackErrorInMeters=\u2205 XTE
368
-averageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle.
369
-raceAverageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen in diesem Rennen; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle.
368
+averageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
369
+raceAverageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen in diesem Rennen; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
370 370
averageSignedCrossTrackErrorInMeters=\u2205 XTE +/-
371 371
averageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten.
372 372
raceAverageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen diesem Rennen, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten.
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java
... ...
@@ -268,11 +268,11 @@ public class LegColumn extends ExpandableSortableColumn<String> {
268 268
detailColumnStyle, leaderboardPanel));
269 269
result.put(DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS,
270 270
new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS,
271
- new DoubleDetailTypeExtractor(e -> e.averageAbsoluteCrossTrackErrorInMeters), detailHeaderStyle,
271
+ new DoubleDetailTypeExtractor(e -> e.currentOrAverageAbsoluteCrossTrackErrorInMeters), detailHeaderStyle,
272 272
detailColumnStyle, leaderboardPanel));
273 273
result.put(DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS,
274 274
new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS,
275
- new DoubleDetailTypeExtractor(e -> e.averageSignedCrossTrackErrorInMeters), detailHeaderStyle,
275
+ new DoubleDetailTypeExtractor(e -> e.currentOrAverageSignedCrossTrackErrorInMeters), detailHeaderStyle,
276 276
detailColumnStyle, leaderboardPanel));
277 277
result.put(DetailType.EXPEDITION_LEG_AWA,
278 278
new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.EXPEDITION_LEG_AWA,
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java
... ...
@@ -49,6 +49,8 @@ import com.sap.sse.datamining.shared.impl.dto.StoredDataMiningQueryDTOImpl;
49 49
import com.sap.sse.datamining.ui.client.DataMiningService;
50 50
import com.sap.sse.gwt.server.ProxiedRemoteServiceServlet;
51 51
import com.sap.sse.i18n.ResourceBundleStringMessages;
52
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
53
+import com.sap.sse.replication.ReplicationService;
52 54
import com.sap.sse.security.SecurityService;
53 55
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions;
54 56
import com.sap.sse.util.ServiceTrackerFactory;
... ...
@@ -58,14 +60,16 @@ public class DataMiningServiceImpl extends ProxiedRemoteServiceServlet implement
58 60
59 61
private final BundleContext context;
60 62
private final ServiceTracker<DataMiningServer, DataMiningServer> dataMiningServerTracker;
61
- private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
63
+ private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
62 64
private final StoredDataMiningQueryPersister storedDataMiningQueryPersistor;
63 65
private final DataMiningDTOFactory dtoFactory;
64 66
65 67
public DataMiningServiceImpl() {
66 68
context = Activator.getDefault();
67 69
dataMiningServerTracker = createAndOpenDataMiningServerTracker(context);
68
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
70
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
71
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
72
+ securityServiceTracker.open();
69 73
storedDataMiningQueryPersistor = new StoredDataMiningQueryPersisterImpl(getSecurityService(),
70 74
dataMiningServerTracker);
71 75
dtoFactory = new DataMiningDTOFactory();
... ...
@@ -84,7 +88,11 @@ public class DataMiningServiceImpl extends ProxiedRemoteServiceServlet implement
84 88
}
85 89
86 90
private SecurityService getSecurityService() {
87
- return securityServiceTracker.getService();
91
+ try {
92
+ return securityServiceTracker.getInitializedService(0);
93
+ } catch (InterruptedException e) {
94
+ throw new RuntimeException(e);
95
+ }
88 96
}
89 97
90 98
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
... ...
@@ -394,7 +394,6 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
394 394
import com.sap.sailing.domain.regattalog.RegattaLogStore;
395 395
import com.sap.sailing.domain.resultimport.ResultUrlProvider;
396 396
import com.sap.sailing.domain.sharding.ShardingContext;
397
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
398 397
import com.sap.sailing.domain.swisstimingadapter.StartList;
399 398
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
400 399
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory;
... ...
@@ -601,6 +600,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
601 600
import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler;
602 601
import com.sap.sailing.server.security.SailingViewerRole;
603 602
import com.sap.sailing.server.util.WaitForTrackedRaceUtil;
603
+import com.sap.sailing.shared.server.SharedSailingData;
604 604
import com.sap.sailing.simulator.Path;
605 605
import com.sap.sailing.simulator.PolarDiagram;
606 606
import com.sap.sailing.simulator.SimulationResults;
... ...
@@ -658,6 +658,7 @@ import com.sap.sse.pairinglist.PairingList;
658 658
import com.sap.sse.pairinglist.PairingListTemplate;
659 659
import com.sap.sse.pairinglist.impl.PairingListTemplateImpl;
660 660
import com.sap.sse.qrcode.QRCodeGenerationUtil;
661
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
661 662
import com.sap.sse.replication.OperationWithResult;
662 663
import com.sap.sse.replication.ReplicaDescriptor;
663 664
import com.sap.sse.replication.Replicable;
... ...
@@ -706,7 +707,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
706 707
707 708
private static final long serialVersionUID = 9031688830194537489L;
708 709
709
- private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
710
+ private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker;
710 711
711 712
private final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker;
712 713
... ...
@@ -733,9 +734,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
733 734
private final ServiceTracker<DeviceIdentifierStringSerializationHandler, DeviceIdentifierStringSerializationHandler>
734 735
deviceIdentifierStringSerializationHandlerTracker;
735 736
736
- private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
737
+ private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
737 738
738
- private final ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker;
739
+ private final FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker;
739 740
740 741
private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory;
741 742
... ...
@@ -778,17 +779,23 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
778 779
BundleContext context = Activator.getDefault();
779 780
Activator activator = Activator.getInstance();
780 781
quickRanksLiveCache = new QuickRanksLiveCache(this);
781
- racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
782
- sharedSailingDataTracker = ServiceTrackerFactory.createAndOpen(context, SharedSailingData.class);
783
- windFinderTrackerFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class);
784 782
replicationServiceTracker = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class);
783
+ racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class,
784
+ /* customizer */ null, replicationServiceTracker);
785
+ racingEventServiceTracker.open();
786
+ sharedSailingDataTracker = new FullyInitializedReplicableTracker<>(context, SharedSailingData.class,
787
+ /* service tracker customizer */ null, replicationServiceTracker);
788
+ sharedSailingDataTracker.open();
789
+ windFinderTrackerFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class);
785 790
swissTimingAdapterTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class);
786 791
tractracAdapterTracker = ServiceTrackerFactory.createAndOpen(context, TracTracAdapterFactory.class);
787 792
raceLogTrackingAdapterTracker = ServiceTrackerFactory.createAndOpen(context,
788 793
RaceLogTrackingAdapterFactory.class);
789 794
deviceIdentifierStringSerializationHandlerTracker = ServiceTrackerFactory.createAndOpen(context,
790 795
DeviceIdentifierStringSerializationHandler.class);
791
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
796
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
797
+ /* service tracker customizer */ null, replicationServiceTracker);
798
+ securityServiceTracker.open();
792 799
igtimiAdapterTracker = ServiceTrackerFactory.createAndOpen(context, IgtimiConnectionFactory.class);
793 800
baseDomainFactory = getService().getBaseDomainFactory();
794 801
mongoObjectFactory = getService().getMongoObjectFactory();
... ...
@@ -2776,7 +2783,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2776 2783
2777 2784
protected RacingEventService getService() {
2778 2785
try {
2779
- return racingEventServiceTracker.waitForService(0);
2786
+ return racingEventServiceTracker.getInitializedService(0);
2780 2787
} catch (InterruptedException e) {
2781 2788
throw new RuntimeException(e);
2782 2789
} // grab the service
... ...
@@ -2784,7 +2791,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2784 2791
2785 2792
protected SharedSailingData getSharedSailingData() {
2786 2793
try {
2787
- return sharedSailingDataTracker.waitForService(0);
2794
+ return sharedSailingDataTracker.getInitializedService(0);
2788 2795
} catch (InterruptedException e) {
2789 2796
throw new RuntimeException(e);
2790 2797
} // grab the service
... ...
@@ -2800,7 +2807,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2800 2807
2801 2808
protected SecurityService getSecurityService() {
2802 2809
try {
2803
- return securityServiceTracker.waitForService(0);
2810
+ return securityServiceTracker.getInitializedService(0);
2804 2811
} catch (InterruptedException e) {
2805 2812
throw new RuntimeException(e);
2806 2813
}
... ...
@@ -4467,12 +4474,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
4467 4474
// The queue name must always be the same for this server. In order to achieve
4468 4475
// this we're using the unique server identifier
4469 4476
final ReplicationService replicationService = getReplicationService();
4470
- replicationService.startToReplicateFrom(replicationService.createReplicationMasterDescriptor(messagingHost,
4471
- masterHostName, exchangeName, servletPort, messagingPort,
4472
- /* use local server identifier as queue name */ replicationService.getServerIdentifier().toString(),
4473
- RemoteServerUtil.resolveBearerTokenForRemoteServer(masterHostName, servletPort, usernameOrNull,
4474
- passwordOrNull),
4475
- replicationService.getAllReplicables()));
4477
+ replicationService.setReplicationStarting(true);
4478
+ try {
4479
+ replicationService.startToReplicateFrom(replicationService.createReplicationMasterDescriptor(messagingHost,
4480
+ masterHostName, exchangeName, servletPort, messagingPort,
4481
+ /* use local server identifier as queue name */ replicationService.getServerIdentifier().toString(),
4482
+ RemoteServerUtil.resolveBearerTokenForRemoteServer(masterHostName, servletPort, usernameOrNull,
4483
+ passwordOrNull),
4484
+ replicationService.getAllReplicables()));
4485
+ } finally {
4486
+ replicationService.setReplicationStarting(false);
4487
+ }
4476 4488
}
4477 4489
4478 4490
@Override
... ...
@@ -5324,15 +5336,15 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
5324 5336
@Override
5325 5337
public List<UrlDTO> getResultImportUrls(String resultProviderName) {
5326 5338
final List<UrlDTO> result = new ArrayList<>();
5327
- SecurityService securityService = getSecurityService();
5339
+ SecurityService securityService = getSecurityService();
5328 5340
Iterable<URL> allUrlsReadableBySubject = getService().getResultImportUrls(resultProviderName);
5329
- for (URL url : allUrlsReadableBySubject) {
5330
- QualifiedObjectIdentifier objId = SecuredDomainType.RESULT_IMPORT_URL.getQualifiedObjectIdentifier(
5341
+ for (URL url : allUrlsReadableBySubject) {
5342
+ QualifiedObjectIdentifier objId = SecuredDomainType.RESULT_IMPORT_URL.getQualifiedObjectIdentifier(
5331 5343
new TypeRelativeObjectIdentifier(resultProviderName, url.toString()));
5332
- UrlDTO urlDTO = new UrlDTO(url.toString());
5333
- SecurityDTOUtil.addSecurityInformation(securityService, urlDTO, objId);
5334
- result.add(urlDTO);
5335
- }
5344
+ UrlDTO urlDTO = new UrlDTO(url.toString());
5345
+ SecurityDTOUtil.addSecurityInformation(securityService, urlDTO, objId);
5346
+ result.add(urlDTO);
5347
+ }
5336 5348
return result;
5337 5349
}
5338 5350
... ...
@@ -5345,12 +5357,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
5345 5357
return new URL(urlDto.getUrl());
5346 5358
} catch (MalformedURLException e) {
5347 5359
return null;
5348
- }
5360
+ }
5349 5361
})
5350 5362
.filter(url -> url != null)
5351 5363
.collect(Collectors.toSet());
5352 5364
racingEventService.removeResultImportURLs(resultProviderName, urlsToRemove);
5353
- }
5365
+ }
5354 5366
5355 5367
@Override
5356 5368
public void addResultImportUrl(String resultProviderName, UrlDTO urlDTO) throws Exception {
... ...
@@ -5359,7 +5371,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
5359 5371
.orElseThrow(() -> new IllegalStateException("ResultUrlProvider not found: " + resultProviderName));
5360 5372
final URL url = resultUrlProvider.resolveUrl(urlDTO.getUrl());
5361 5373
racingEventService.addResultImportUrl(resultProviderName, url);
5362
- }
5374
+ }
5363 5375
5364 5376
@Override
5365 5377
public String validateResultImportUrl(String resultProviderName, UrlDTO urlDTO) {
java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java
... ...
@@ -84,7 +84,7 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT
84 84
85 85
private final Set<OperationWithResult<PolarDataService, ?>> operationsSentToMasterForReplication;
86 86
87
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
87
+ private volatile boolean currentlyFillingFromInitialLoad;
88 88
89 89
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
90 90
... ...
@@ -103,6 +103,7 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT
103 103
*/
104 104
public PolarDataServiceImpl() {
105 105
resetState();
106
+ this.currentlyFillingFromInitialLoad = false;
106 107
this.operationsSentToMasterForReplication = new HashSet<>();
107 108
this.operationExecutionListeners = new ConcurrentHashMap<>();
108 109
}
... ...
@@ -322,12 +323,12 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT
322 323
323 324
@Override
324 325
public boolean isCurrentlyFillingFromInitialLoad() {
325
- return currentlyFillingFromInitialLoad.get();
326
+ return currentlyFillingFromInitialLoad;
326 327
}
327 328
328 329
@Override
329 330
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
330
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
331
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
331 332
}
332 333
333 334
@Override
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/AclRevokeAnonymousTest.java
... ...
@@ -0,0 +1,64 @@
1
+package com.sap.sailing.selenium.api.test;
2
+
3
+import static com.sap.sailing.selenium.api.core.ApiContext.SECURITY_CONTEXT;
4
+import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT;
5
+import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext;
6
+import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext;
7
+import static com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage.goToPage;
8
+
9
+import org.hamcrest.Matchers;
10
+import org.junit.Assert;
11
+import org.junit.Before;
12
+import org.junit.Test;
13
+import org.openqa.selenium.WebElement;
14
+
15
+import com.sap.sailing.domain.common.CompetitorRegistrationType;
16
+import com.sap.sailing.selenium.api.core.ApiContext;
17
+import com.sap.sailing.selenium.api.event.EventApi;
18
+import com.sap.sailing.selenium.api.event.SecurityApi;
19
+import com.sap.sailing.selenium.pages.adminconsole.AclPopupPO;
20
+import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
21
+import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanelPO;
22
+import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanelPO.EventEntryPO;
23
+import com.sap.sailing.selenium.test.AbstractSeleniumTest;
24
+
25
+public class AclRevokeAnonymousTest extends AbstractSeleniumTest {
26
+
27
+ private ApiContext ownerCtx;
28
+
29
+ private final SecurityApi securityApi = new SecurityApi();
30
+ private final EventApi eventApi = new EventApi();
31
+
32
+ private static final String EVENT_NAME = "Super exclusive regatta - invited competitors only";
33
+ private static final String BOAT_CLASS = "GC 32";
34
+ private AdminConsolePage adminConsole;
35
+
36
+ @Before
37
+ public void setUp() {
38
+ clearState(getContextRoot());
39
+ super.setUp();
40
+ final ApiContext adminSecurityCtx = createAdminApiContext(getContextRoot(), SECURITY_CONTEXT);
41
+ securityApi.createUser(adminSecurityCtx, "donald", "Donald Duck", null, "daisy0815");
42
+ ownerCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "donald", "daisy0815");
43
+ adminConsole = goToPage(getWebDriver(), getContextRoot());
44
+ adminConsole.goToLocalServerPanel().setSelfServiceServer(true);
45
+ eventApi.createEvent(ownerCtx, EVENT_NAME, BOAT_CLASS, CompetitorRegistrationType.CLOSED, "Some special place");
46
+ }
47
+
48
+ @Test
49
+ public void test() {
50
+ EventConfigurationPanelPO eventPanel = adminConsole.goToEvents();
51
+ EventEntryPO eventEntry = eventPanel.getEventEntry(EVENT_NAME);
52
+ AclPopupPO aclPopup = eventEntry.openAclPopup();
53
+ aclPopup.addUserGroup(""); // add empty user group -> anonymous group
54
+ WebElement deniedInput = aclPopup.getDeniedActionsInput();
55
+ WebElement allowedInput = aclPopup.getAllowedActionsInput();
56
+ Assert.assertThat(deniedInput.isEnabled(), Matchers.equalTo(false));
57
+ Assert.assertThat(allowedInput.isEnabled(), Matchers.equalTo(true));
58
+
59
+ aclPopup.addUserGroup("admin-tenant");
60
+ Assert.assertThat(deniedInput.isEnabled(), Matchers.equalTo(true));
61
+ Assert.assertThat(allowedInput.isEnabled(), Matchers.equalTo(true));
62
+ }
63
+
64
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/AclPopupPO.java
... ...
@@ -0,0 +1,36 @@
1
+package com.sap.sailing.selenium.pages.adminconsole;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.pages.PageArea;
8
+
9
+public class AclPopupPO extends PageArea {
10
+
11
+ public static final String INPUT_SUGGEST_BOX = "InputSuggestBox";
12
+ public static final String ALLOWED_ACTIONS_CONTAINER = "allowedActionsContainer";
13
+ public static final String DENIED_ACTIONS_CONTAINER = "deniedActionsContainer";
14
+ public static final String ADD_USER_GROUP_BUTTON = "AddUserGroupButton";
15
+ public static final String SUGGEST_USER_GROUP_INPUT = "SuggestUserGroupInput";
16
+
17
+ public AclPopupPO(WebDriver driver, WebElement root) {
18
+ super(driver, root);
19
+ }
20
+
21
+ public void addUserGroup(String groupName) {
22
+ driver.findElement(new BySeleniumId(SUGGEST_USER_GROUP_INPUT)).sendKeys(groupName);
23
+ driver.findElement(new BySeleniumId(ADD_USER_GROUP_BUTTON)).click();
24
+ }
25
+
26
+ public WebElement getDeniedActionsInput() {
27
+ WebElement parent = context.findElement(new BySeleniumId(DENIED_ACTIONS_CONTAINER));
28
+ return parent.findElement(new BySeleniumId(INPUT_SUGGEST_BOX));
29
+ }
30
+
31
+ public WebElement getAllowedActionsInput() {
32
+ WebElement parent = context.findElement(new BySeleniumId(ALLOWED_ACTIONS_CONTAINER));
33
+ return parent.findElement(new BySeleniumId(INPUT_SUGGEST_BOX));
34
+ }
35
+
36
+}
... ...
\ No newline at end of file
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java
... ...
@@ -3,12 +3,14 @@ package com.sap.sailing.selenium.pages.adminconsole.event;
3 3
import java.util.Date;
4 4
5 5
import org.openqa.selenium.By;
6
+import org.openqa.selenium.By.ByName;
6 7
import org.openqa.selenium.WebDriver;
7 8
import org.openqa.selenium.WebElement;
8 9
9 10
import com.sap.sailing.selenium.core.BySeleniumId;
10 11
import com.sap.sailing.selenium.core.FindBy;
11 12
import com.sap.sailing.selenium.pages.PageArea;
13
+import com.sap.sailing.selenium.pages.adminconsole.AclPopupPO;
12 14
import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardGroupCreateDialogPO;
13 15
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaCreateDialogPO;
14 16
import com.sap.sailing.selenium.pages.common.ConfirmDialogPO;
... ...
@@ -30,6 +32,9 @@ public class EventConfigurationPanelPO extends PageArea {
30 32
31 33
public static class EventEntryPO extends DataEntryPO {
32 34
35
+ @FindBy(how = ByName.class, using = "CHANGE_ACL")
36
+ private WebElement aclButton;
37
+
33 38
public EventEntryPO(CellTablePO<?> table, WebElement element) {
34 39
super(table, element);
35 40
}
... ...
@@ -41,7 +46,14 @@ public class EventConfigurationPanelPO extends PageArea {
41 46
42 47
public String getEventURL() {
43 48
return getWebElement().findElement(By.xpath(".//td/div/a")).getAttribute("href");
44
- }
49
+ }
50
+
51
+ public AclPopupPO openAclPopup() {
52
+ aclButton.click();
53
+ waitForElement("AclDialog");
54
+ return new AclPopupPO(this.driver, driver.findElement(new BySeleniumId(("AclDialog"))));
55
+ }
56
+
45 57
}
46 58
47 59
@FindBy(how = BySeleniumId.class, using = "RefreshEventsButton")
java/com.sap.sailing.server.gateway.serialization/META-INF/MANIFEST.MF
... ...
@@ -15,7 +15,8 @@ Require-Bundle: com.sap.sailing.domain.common,
15 15
com.sap.sailing.server.gateway.serialization.shared.android,
16 16
com.sap.sse.security,
17 17
com.sap.sse.security.common,
18
- com.sap.sse.replication
18
+ com.sap.sse.replication,
19
+ com.sap.sailing.shared.server
19 20
Export-Package: com.sap.sailing.server.gateway.deserialization.impl,
20 21
com.sap.sailing.server.gateway.serialization,
21 22
com.sap.sailing.server.gateway.serialization.impl,
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CourseConfigurationBuilder.java
... ...
@@ -52,9 +52,9 @@ import com.sap.sailing.domain.coursetemplate.impl.MarkTemplateBasedMarkConfigura
52 52
import com.sap.sailing.domain.coursetemplate.impl.RegattaMarkConfigurationImpl;
53 53
import com.sap.sailing.domain.coursetemplate.impl.WaypointWithMarkConfigurationImpl;
54 54
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
55
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
56 55
import com.sap.sailing.domain.tracking.GPSFixTrack;
57 56
import com.sap.sailing.domain.tracking.TrackedRace;
57
+import com.sap.sailing.shared.server.SharedSailingData;
58 58
import com.sap.sse.common.TimeRange;
59 59
import com.sap.sse.common.Util.Triple;
60 60
import com.sap.sse.common.WithID;
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CourseConfigurationJsonDeserializer.java
... ...
@@ -22,10 +22,10 @@ import com.sap.sailing.domain.coursetemplate.MarkConfigurationRequestAnnotation.
22 22
import com.sap.sailing.domain.coursetemplate.Positioning;
23 23
import com.sap.sailing.domain.coursetemplate.RepeatablePart;
24 24
import com.sap.sailing.domain.coursetemplate.impl.MarkConfigurationRequestAnnotationImpl.MarkRoleCreationRequestImpl;
25
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
26 25
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
27 26
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
28 27
import com.sap.sailing.server.gateway.serialization.impl.CourseConfigurationJsonSerializer;
28
+import com.sap.sailing.shared.server.SharedSailingData;
29 29
30 30
public class CourseConfigurationJsonDeserializer implements JsonDeserializer<CourseConfiguration<MarkConfigurationRequestAnnotation>> {
31 31
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
... ...
@@ -20,10 +20,10 @@ import com.sap.sailing.domain.base.Regatta;
20 20
import com.sap.sailing.domain.common.ScoreCorrectionProvider;
21 21
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
22 22
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
23
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
24 23
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
25 24
import com.sap.sailing.domain.tracking.TrackedRace;
26 25
import com.sap.sailing.server.interfaces.RacingEventService;
26
+import com.sap.sailing.shared.server.SharedSailingData;
27 27
import com.sap.sse.InvalidDateException;
28 28
import com.sap.sse.common.TimePoint;
29 29
import com.sap.sse.common.TypeBasedServiceFinderFactory;
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/RestServletContainer.java
... ...
@@ -7,8 +7,8 @@ import javax.ws.rs.core.Application;
7 7
import org.osgi.framework.BundleContext;
8 8
import org.osgi.util.tracker.ServiceTracker;
9 9
10
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
11 10
import com.sap.sailing.server.interfaces.RacingEventService;
11
+import com.sap.sailing.shared.server.SharedSailingData;
12 12
import com.sap.sse.datamining.DataMiningServer;
13 13
import com.sap.sse.replication.ReplicationService;
14 14
import com.sap.sse.security.SecurityService;
java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF
... ...
@@ -37,5 +37,6 @@ Require-Bundle: com.sap.sailing.server,
37 37
com.sun.jersey;bundle-version="1.17.0",
38 38
com.sap.sse.security.testsupport,
39 39
com.sap.sailing.domain.racelogtrackingadapter,
40
- com.sap.sailing.resultimport;bundle-version="1.0.0"
40
+ com.sap.sailing.shared.server,
41
+ com.sap.sailing.resultimport
41 42
Automatic-Module-Name: com.sap.sailing.server.replication.test
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java
... ...
@@ -55,8 +55,10 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat
55 55
@Override public DomainFactory getBaseDomainFactory() { return baseDomainFactory; }
56 56
@Override public CompetitorAndBoatStore getCompetitorAndBoatStore() { return getBaseDomainFactory().getCompetitorAndBoatStore(); }
57 57
};
58
- }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null,
59
- /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false, null, /* sharedSailingData */ null,
58
+ }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE,
59
+ EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null,
60
+ /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false,
61
+ /* security service tracker */ null, /* sharedSailingData */ null, /* replicationServiceTracker */ null,
60 62
/* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null);
61 63
}
62 64
... ...
@@ -75,9 +77,10 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat
75 77
@Override public DomainFactory getBaseDomainFactory() { return domainObjectFactory.getBaseDomainFactory(); }
76 78
@Override public CompetitorAndBoatStore getCompetitorAndBoatStore() { return getBaseDomainFactory().getCompetitorAndBoatStore(); }
77 79
};
78
- }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE,
79
- /* serviceFinderFactory */ null, null, /* sailingNotificationService */ null,
80
- /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false, null, /* sharedSailingData */ null,
80
+ }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE,
81
+ EmptySensorFixStore.INSTANCE, /* serviceFinderFactory */ null, null,
82
+ /* sailingNotificationService */ null, /* trackedRaceStatisticsCache */ null,
83
+ /* restoreTrackedRaces */ false, /* security service tracker */ null, /* sharedSailingData */ null, /* replicationServiceTracker */ null,
81 84
/* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null);
82 85
}
83 86
}
java/com.sap.sailing.server/META-INF/MANIFEST.MF
... ...
@@ -42,6 +42,7 @@ Require-Bundle: com.sap.sailing.domain,
42 42
com.sap.sailing.server.interface,
43 43
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
44 44
com.sap.sse.security.common,
45
+ com.sap.sailing.shared.server,
45 46
com.sap.sailing.resultimport
46 47
Bundle-ClassPath: .
47 48
Export-Package: com.sap.sailing.server,
java/com.sap.sailing.server/src/com/sap/sailing/server/hierarchy/SailingHierarchyOwnershipUpdater.java
... ...
@@ -274,14 +274,13 @@ public class SailingHierarchyOwnershipUpdater {
274 274
private static UserGroup copyUserGroup(UserGroup userGroupToCopy, String name, SecurityService securitySerice,
275 275
RacingEventService service) throws UserGroupManagementException {
276 276
// explicitly loading the current version of the group in case the given instance e.g. originates from the UI
277
- // and is possible out of date.
277
+ // and is possibly out of date.
278 278
final UUID newGroupId = UUID.randomUUID();
279 279
return securitySerice.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
280 280
SecuredSecurityTypes.USER_GROUP, UserGroupImpl.getTypeRelativeObjectIdentifier(newGroupId), name, () -> {
281 281
final UserGroup createdUserGroup = securitySerice.createUserGroup(newGroupId, name);
282 282
securitySerice.copyUsersAndRoleAssociations(userGroupToCopy, createdUserGroup,
283 283
new RoleCopyListener() {
284
-
285 284
@Override
286 285
public void onRoleCopy(User user, Role existingRole, Role copyRole) {
287 286
TypeRelativeObjectIdentifier existingAssociationTypeIdentifier = PermissionAndRoleAssociation
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
... ...
@@ -41,7 +41,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandl
41 41
import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl;
42 42
import com.sap.sailing.domain.polars.PolarDataService;
43 43
import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier;
44
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
45 44
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
46 45
import com.sap.sailing.domain.windestimation.WindEstimationFactoryService;
47 46
import com.sap.sailing.resultimport.ResultUrlRegistry;
... ...
@@ -56,6 +55,7 @@ import com.sap.sailing.server.notification.impl.SailingNotificationServiceImpl;
56 55
import com.sap.sailing.server.security.SailingViewerRole;
57 56
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache;
58 57
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCacheImpl;
58
+import com.sap.sailing.shared.server.SharedSailingData;
59 59
import com.sap.sse.MasterDataImportClassLoaderService;
60 60
import com.sap.sse.common.TypeBasedServiceFinder;
61 61
import com.sap.sse.common.Util;
... ...
@@ -63,7 +63,9 @@ import com.sap.sse.mail.MailService;
63 63
import com.sap.sse.mail.queue.MailQueue;
64 64
import com.sap.sse.mail.queue.impl.ExecutorMailQueue;
65 65
import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory;
66
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
66 67
import com.sap.sse.replication.Replicable;
68
+import com.sap.sse.replication.ReplicationService;
67 69
import com.sap.sse.security.SecurityInitializationCustomizer;
68 70
import com.sap.sse.security.SecurityService;
69 71
import com.sap.sse.security.interfaces.PreferenceConverter;
... ...
@@ -111,9 +113,11 @@ public class Activator implements BundleActivator {
111 113
112 114
private ServiceTracker<MailService, MailService> mailServiceTracker;
113 115
114
- private ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
116
+ private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
115 117
116
- private ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker;
118
+ private FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker;
119
+
120
+ private ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker;
117 121
118 122
public Activator() {
119 123
clearPersistentCompetitors = Boolean
... ...
@@ -130,13 +134,19 @@ public class Activator implements BundleActivator {
130 134
extenderBundleTracker = new ExtenderBundleTracker(context);
131 135
extenderBundleTracker.open();
132 136
mailServiceTracker = ServiceTrackerFactory.createAndOpen(context, MailService.class);
133
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
137
+ replicationServiceTracker = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class);
138
+ sharedSailingDataTracker = new FullyInitializedReplicableTracker<>(context, SharedSailingData.class,
139
+ /* customizer */ null, replicationServiceTracker);
140
+ sharedSailingDataTracker.open();
141
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
142
+ /* customizer */ null, replicationServiceTracker);
143
+ securityServiceTracker.open();
134 144
if (securityServiceTracker != null) {
135 145
new Thread("Racingevent wait for securityservice for migration thread") {
136 146
public void run() {
137 147
try {
138 148
// only continue once we have the service, as some of the services require it to start properly
139
- securityServiceTracker.waitForService(0);
149
+ securityServiceTracker.getInitializedService(0);
140 150
internalStartBundle(context);
141 151
} catch (Exception e) {
142 152
logger.log(Level.SEVERE, "Could not start RacingEvent service properly", e);
... ...
@@ -200,13 +210,15 @@ public class Activator implements BundleActivator {
200 210
mailQueue.stop();
201 211
mailServiceTracker.close();
202 212
sharedSailingDataTracker.close();
213
+ replicationServiceTracker.close();
203 214
securityServiceTracker.close();
204 215
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
205 216
mbs.unregisterMBean(mBeanName);
206 217
}
207 218
208 219
private void internalStartBundle(BundleContext context) throws MalformedURLException, MalformedObjectNameException,
209
- InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
220
+ InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InterruptedException {
221
+ assert securityServiceTracker.getInitializedService(0) != null; // callers must call securityServiceTracker.waitForService(0) before calling this method
210 222
mailQueue = new ExecutorMailQueue(mailServiceTracker);
211 223
notificationService = new SailingNotificationServiceImpl(context, mailQueue);
212 224
trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context);
... ...
@@ -236,7 +248,6 @@ public class Activator implements BundleActivator {
236 248
// this code block is not run, and the test case can inject some other type of finder
237 249
// instead.
238 250
serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context);
239
- sharedSailingDataTracker = ServiceTrackerFactory.createAndOpen(context, SharedSailingData.class);
240 251
ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker =
241 252
ServiceTrackerFactory.createAndOpen(context, ScoreCorrectionProvider.class);
242 253
ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker = ServiceTrackerFactory
... ...
@@ -244,7 +255,7 @@ public class Activator implements BundleActivator {
244 255
racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors,
245 256
serviceFinderFactory, trackedRegattaListener, notificationService,
246 257
trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker,
247
- sharedSailingDataTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker);
258
+ sharedSailingDataTracker, replicationServiceTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker);
248 259
notificationService.setRacingEventService(racingEventService);
249 260
masterDataImportClassLoaderServiceTracker = new ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService>(
250 261
context, MasterDataImportClassLoaderService.class,
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java
... ...
@@ -18,8 +18,6 @@ import java.util.logging.Level;
18 18
import java.util.logging.Logger;
19 19
import java.util.stream.Collectors;
20 20
21
-import org.osgi.util.tracker.ServiceTracker;
22
-
23 21
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
24 22
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
25 23
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
... ...
@@ -88,22 +86,23 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
88 86
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
89 87
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier;
90 88
import com.sap.sailing.domain.racelogtracking.impl.PingDeviceIdentifierImpl;
91
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
92 89
import com.sap.sailing.domain.tracking.TrackedRace;
93 90
import com.sap.sailing.server.gateway.deserialization.impl.CourseConfigurationBuilder;
94 91
import com.sap.sailing.server.interfaces.CourseAndMarkConfigurationFactory;
92
+import com.sap.sailing.shared.server.SharedSailingData;
95 93
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
96 94
import com.sap.sse.common.TimePoint;
97 95
import com.sap.sse.common.Timed;
98 96
import com.sap.sse.common.Util;
99 97
import com.sap.sse.common.Util.Pair;
98
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
100 99
import com.sap.sse.security.shared.impl.UserGroup;
101 100
102 101
public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfigurationFactory {
103 102
104 103
private static final Logger logger = Logger.getLogger(CourseAndMarkConfigurationFactoryImpl.class.getName());
105 104
106
- private final ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker;
105
+ private final FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker;
107 106
private final SensorFixStore sensorFixStore;
108 107
109 108
/**
... ...
@@ -117,7 +116,7 @@ public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfi
117 116
private final DomainFactory domainFactory;
118 117
119 118
public CourseAndMarkConfigurationFactoryImpl(
120
- ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker,
119
+ FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker,
121 120
SensorFixStore sensorFixStore, RaceLogResolver raceLogResolver, DomainFactory domainFactory) {
122 121
this.sharedSailingDataTracker = sharedSailingDataTracker;
123 122
this.domainFactory = domainFactory;
... ...
@@ -139,7 +138,15 @@ public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfi
139 138
}
140 139
141 140
private SharedSailingData getSharedSailingData() {
142
- return sharedSailingDataTracker.getService();
141
+ SharedSailingData result;
142
+ try {
143
+ result = sharedSailingDataTracker.getInitializedService(0);
144
+ } catch (InterruptedException e) {
145
+ logger.log(Level.SEVERE, "Interrupted while waiting for a fully initialized SharedSailingData service; "
146
+ + "continuing with null, probably causing a NullPointerException along the way", e);
147
+ result = null;
148
+ }
149
+ return result;
143 150
}
144 151
145 152
private CourseTemplate resolveCourseTemplateSafe(CourseBase course) {
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
... ...
@@ -189,7 +189,6 @@ import com.sap.sailing.domain.regattalike.IsRegattaLike;
189 189
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
190 190
import com.sap.sailing.domain.regattalog.RegattaLogStore;
191 191
import com.sap.sailing.domain.resultimport.ResultUrlProvider;
192
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
193 192
import com.sap.sailing.domain.statistics.Statistics;
194 193
import com.sap.sailing.domain.tracking.AddResult;
195 194
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
... ...
@@ -209,13 +208,13 @@ import com.sap.sailing.domain.tracking.TrackedRace;
209 208
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
210 209
import com.sap.sailing.domain.tracking.TrackedRegatta;
211 210
import com.sap.sailing.domain.tracking.TrackedRegattaListener;
211
+import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
212 212
import com.sap.sailing.domain.tracking.WindStore;
213 213
import com.sap.sailing.domain.tracking.WindTracker;
214 214
import com.sap.sailing.domain.tracking.WindTrackerFactory;
215 215
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
216 216
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
217 217
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
218
-import com.sap.sailing.domain.tracking.TrackingConnectorInfo;
219 218
import com.sap.sailing.domain.windestimation.WindEstimationFactoryService;
220 219
import com.sap.sailing.expeditionconnector.ExpeditionTrackerFactory;
221 220
import com.sap.sailing.resultimport.ResultUrlRegistry;
... ...
@@ -286,6 +285,7 @@ import com.sap.sailing.server.statistics.StatisticsCalculator;
286 285
import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache;
287 286
import com.sap.sailing.server.tagging.TaggingServiceFactory;
288 287
import com.sap.sailing.server.util.EventUtil;
288
+import com.sap.sailing.shared.server.SharedSailingData;
289 289
import com.sap.sse.ServerInfo;
290 290
import com.sap.sse.common.Distance;
291 291
import com.sap.sse.common.Duration;
... ...
@@ -309,6 +309,7 @@ import com.sap.sse.pairinglist.PairingFrameProvider;
309 309
import com.sap.sse.pairinglist.PairingList;
310 310
import com.sap.sse.pairinglist.PairingListTemplate;
311 311
import com.sap.sse.pairinglist.PairingListTemplateFactory;
312
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
312 313
import com.sap.sse.replication.OperationExecutionListener;
313 314
import com.sap.sse.replication.OperationWithResult;
314 315
import com.sap.sse.replication.OperationWithResultWithIdWrapper;
... ...
@@ -519,7 +520,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
519 520
520 521
private Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication;
521 522
522
- private boolean currentlyFillingFromInitialLoad = false;
523
+ private volatile boolean currentlyFillingFromInitialLoad;
523 524
524 525
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
525 526
... ...
@@ -534,7 +535,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
534 535
private long numberOfTrackedRacesToRestore;
535 536
536 537
private final AtomicInteger numberOfTrackedRacesRestored;
537
-
538
+
538 539
private final ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker;
539 540
540 541
private final ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker;
... ...
@@ -565,8 +566,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
565 566
*/
566 567
private OperationsToMasterSendingQueue unsentOperationsToMasterSender;
567 568
568
- private ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
569
-
569
+ private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
570
+
570 571
private final CourseAndMarkConfigurationFactory courseAndMarkConfigurationFactory;
571 572
572 573
/**
... ...
@@ -609,7 +610,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
609 610
610 611
public RacingEventServiceImpl(boolean clearPersistentCompetitorAndBoatStore, final TypeBasedServiceFinderFactory serviceFinderFactory, boolean restoreTrackedRaces) {
611 612
this(clearPersistentCompetitorAndBoatStore, serviceFinderFactory, null, /* sailingNotificationService */ null,
612
- /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null,
613
+ /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null,
613 614
/* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null);
614 615
}
615 616
... ...
@@ -636,8 +637,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
636 637
final TypeBasedServiceFinderFactory serviceFinderFactory, TrackedRegattaListenerManager trackedRegattaListener,
637 638
SailingNotificationService sailingNotificationService,
638 639
TrackedRaceStatisticsCache trackedRaceStatisticsCache, boolean restoreTrackedRaces,
639
- ServiceTracker<SecurityService, SecurityService> securityServiceTracker,
640
- ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker,
640
+ FullyInitializedReplicableTracker<SecurityService> securityServiceTracker,
641
+ FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker, ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker,
641 642
ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker,
642 643
ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker) {
643 644
this((final RaceLogAndTrackedRaceResolver raceLogResolver) -> {
... ...
@@ -669,8 +670,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
669 670
}
670 671
};
671 672
}, MediaDBFactory.INSTANCE.getDefaultMediaDB(), null, null, serviceFinderFactory, trackedRegattaListener,
672
- sailingNotificationService, trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker,
673
- sharedSailingDataTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker);
673
+ sailingNotificationService, trackedRaceStatisticsCache, restoreTrackedRaces,
674
+ securityServiceTracker, sharedSailingDataTracker, /* replicationServiceTracker */ null,
675
+ scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker);
674 676
}
675 677
676 678
private RacingEventServiceImpl(final boolean clearPersistentCompetitorStore, WindStore windStore,
... ...
@@ -703,10 +705,11 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
703 705
return competitorStore;
704 706
}
705 707
};
706
- }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory, null,
707
- sailingNotificationService, /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null,
708
- /* sharedSailingDataTracker */ null, /* scoreCorrectionProviderServiceTracker */ null,
709
- /* resultUrlRegistryServiceTracker */ null);
708
+ }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory,
709
+ /* tracked regatta listener */ null,
710
+ sailingNotificationService, /* trackedRaceStatisticsCache */ null, restoreTrackedRaces,
711
+ /* security service tracker */ null, /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null,
712
+ /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null);
710 713
}
711 714
712 715
public RacingEventServiceImpl(final DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory,
... ...
@@ -733,8 +736,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
733 736
return getBaseDomainFactory().getCompetitorAndBoatStore();
734 737
}
735 738
};
736
- }, mediaDB, windStore, sensorFixStore, null, null, /* sailingNotificationService */ null,
737
- /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null,
739
+ }, mediaDB, windStore, sensorFixStore, /* service finder factory */ null,
740
+ /* tracked regatta listener */ null, /* sailingNotificationService */ null,
741
+ /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, /* security service tracker */ null,
742
+ /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null,
738 743
/* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null);
739 744
}
740 745
... ...
@@ -771,11 +776,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
771 776
TypeBasedServiceFinderFactory serviceFinderFactory, TrackedRegattaListenerManager trackedRegattaListener,
772 777
SailingNotificationService sailingNotificationService,
773 778
TrackedRaceStatisticsCache trackedRaceStatisticsCache, boolean restoreTrackedRaces,
774
- ServiceTracker<SecurityService, SecurityService> securityServiceTracker,
775
- ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker,
779
+ FullyInitializedReplicableTracker<SecurityService> securityServiceTracker,
780
+ FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker,
781
+ ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker,
776 782
ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker,
777 783
ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker) {
778 784
logger.info("Created " + this);
785
+ this.currentlyFillingFromInitialLoad = false;
779 786
this.securityServiceTracker = securityServiceTracker;
780 787
this.numberOfTrackedRacesRestored = new AtomicInteger();
781 788
this.resultUrlRegistryServiceTracker = resultUrlRegistryServiceTracker;
... ...
@@ -4776,13 +4783,18 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
4776 4783
return competitorWithBoat;
4777 4784
}
4778 4785
4779
- @Override
4780 4786
/**
4781 4787
* This should only be used for replicable Operations that need access to the SecurityService, all other should
4782 4788
* obtain the SecurityService in another way.
4783 4789
*/
4790
+ @Override
4784 4791
public SecurityService getSecurityService() {
4785
- return securityServiceTracker.getService();
4792
+ try {
4793
+ return securityServiceTracker.getInitializedService(0);
4794
+ } catch (InterruptedException e) {
4795
+ logger.severe("Interrupted while waiting for security service; returning null");
4796
+ return null;
4797
+ }
4786 4798
}
4787 4799
4788 4800
@Override
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/ReplicatingSharedSailingData.java
... ...
@@ -1,51 +0,0 @@
1
-package com.sap.sailing.server.impl;
2
-
3
-import java.net.URL;
4
-import java.util.ArrayList;
5
-import java.util.Map;
6
-import java.util.UUID;
7
-
8
-import com.sap.sailing.domain.common.DeviceIdentifier;
9
-import com.sap.sailing.domain.common.Position;
10
-import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
11
-import com.sap.sailing.domain.coursetemplate.MarkRole;
12
-import com.sap.sailing.domain.coursetemplate.MarkTemplate;
13
-import com.sap.sailing.domain.coursetemplate.RepeatablePart;
14
-import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
15
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
16
-import com.sap.sse.replication.OperationWithResult;
17
-import com.sap.sse.replication.ReplicableWithObjectInputStream;
18
-
19
-public interface ReplicatingSharedSailingData extends SharedSailingData,
20
- ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> {
21
-
22
- Void internalCreateMarkRole(UUID idOfNewMarkRole, String name);
23
-
24
- Void internalCreateMarkProperties(UUID idOfNewMarkProperties, CommonMarkProperties properties,
25
- Iterable<String> tags);
26
-
27
- Void internalUpdateMarkProperties(UUID idOfNewMarkProperties, CommonMarkProperties properties, Position position,
28
- DeviceIdentifier deviceIdentifier, Iterable<String> tags);
29
-
30
- Void internalCreateMarkTemplate(UUID idOfNewMarkTemplate, CommonMarkProperties properties);
31
-
32
- Void internalCreateCourseTemplate(UUID idOfNewCourseTemplate, String courseTemplateName,
33
- Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints,
34
- Map<MarkTemplate, MarkRole> associatedRoles, RepeatablePart optionalRepeatablePart, Iterable<String> tags,
35
- URL optionalImageURL, Integer defaultNumberOfLaps);
36
-
37
- Void internalSetTrackingDeviceIdentifierForMarkProperties(UUID markPropertiesUUID, DeviceIdentifier deviceIdentifier);
38
-
39
- Void internalSetFixedPositionForMarkProperties(UUID markPropertiesUUID, Position position);
40
-
41
- Void internalDeleteMarkProperties(UUID markPropertiesUUID);
42
-
43
- Void internalDeleteCourseTemplate(UUID courseTemplateUUID);
44
-
45
- Void internalRecordUsage(UUID markTemplateId, UUID markPropertiesId);
46
-
47
- Void internalRecordUsage(UUID markPropertiesId, MarkRole roleName);
48
-
49
- Void internalUpdateCourseTemplate(UUID uuid, String name, URL optionalImageURL, ArrayList<String> tags);
50
-
51
-}
java/com.sap.sailing.shared.server.gateway/src/com/sap/sailing/shared/server/gateway/jaxrs/AbstractSailingServerResource.java
... ...
@@ -13,10 +13,10 @@ import com.sap.sailing.domain.base.Fleet;
13 13
import com.sap.sailing.domain.base.RaceColumn;
14 14
import com.sap.sailing.domain.base.RaceDefinition;
15 15
import com.sap.sailing.domain.base.Regatta;
16
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
17 16
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
18 17
import com.sap.sailing.domain.tracking.TrackedRace;
19 18
import com.sap.sailing.server.interfaces.RacingEventService;
19
+import com.sap.sailing.shared.server.SharedSailingData;
20 20
import com.sap.sse.InvalidDateException;
21 21
import com.sap.sse.common.TimePoint;
22 22
import com.sap.sse.common.TypeBasedServiceFinderFactory;
java/com.sap.sailing.shared.server.gateway/src/com/sap/sailing/shared/server/gateway/jaxrs/RestServletContainer.java
... ...
@@ -7,8 +7,8 @@ import javax.ws.rs.core.Application;
7 7
import org.osgi.framework.BundleContext;
8 8
import org.osgi.util.tracker.ServiceTracker;
9 9
10
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
11 10
import com.sap.sailing.server.interfaces.RacingEventService;
11
+import com.sap.sailing.shared.server.SharedSailingData;
12 12
import com.sap.sse.replication.ReplicationService;
13 13
import com.sap.sse.security.SecurityService;
14 14
import com.sun.jersey.spi.container.servlet.ServletContainer;
java/com.sap.sailing.shared.server/META-INF/MANIFEST.MF
... ...
@@ -7,38 +7,26 @@ Bundle-Version: 1.0.0.qualifier
7 7
Bundle-Activator: com.sap.sailing.shared.server.impl.Activator
8 8
Bundle-Vendor: SAP
9 9
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
10
-Import-Package: com.sap.sailing.server.gateway.deserialization,
11
- javax.mail;version="1.4.0",
12
- javax.mail.internet;version="1.4.0",
13
- javax.mail.util;version="1.4.0",
14
- org.json.simple,
15
- org.json.simple.parser,
16
- org.osgi.framework;version="1.3.0",
10
+Import-Package: org.osgi.framework;version="1.3.0",
17 11
org.osgi.util.tracker;version="1.3.1"
18 12
Bundle-ActivationPolicy: lazy
19
-Require-Bundle: com.sap.sailing.domain,
20
- com.sap.sse.mongodb,
21
- com.sap.sailing.domain.common,
13
+Require-Bundle: com.sap.sailing.domain.common,
22 14
com.sap.sailing.domain.shared.android,
23
- com.sap.sailing.server.gateway.serialization,
24 15
com.sap.sse.common,
25
- com.sap.sse.operationaltransformation,
26 16
com.sap.sse.replication.interfaces,
27 17
com.sap.sse,
28
- com.sap.sailing.server.gateway.serialization.shared.android,
29 18
com.sap.sse.shared.android,
30
- com.sap.sse.security,
31 19
com.sap.sse.security.common,
32 20
org.apache.shiro.web;bundle-version="1.2.2",
33 21
org.apache.shiro.core;bundle-version="1.2.2",
34
- javax.servlet;bundle-version="3.1.0",
35
- com.sap.sse.mail,
36
- com.sap.sailing.server.interface,
37
- org.mongodb.mongo-java-driver;bundle-version="3.6.4",
38 22
com.sap.sse.security.common,
39
- com.sap.sailing.shared.persistence
23
+ com.sap.sse.operationaltransformation,
24
+ com.sap.sse.security,
25
+ com.sap.sailing.shared.persistence,
26
+ javax.servlet;bundle-version="3.1.0"
40 27
Bundle-ClassPath: .
41
-Export-Package: com.sap.sailing.shared.server.impl;
28
+Export-Package: com.sap.sailing.shared.server,
29
+ com.sap.sailing.shared.server.impl;
42 30
x-friends:="com.sap.sailing.mongodb.test,
43 31
com.sap.sailing.server.test,
44 32
com.sap.sailing.server.gateway.test,
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/SharedSailingData.java
... ...
@@ -0,0 +1,143 @@
1
+package com.sap.sailing.shared.server;
2
+
3
+import java.net.URL;
4
+import java.util.ArrayList;
5
+import java.util.Map;
6
+import java.util.Optional;
7
+import java.util.UUID;
8
+
9
+import com.sap.sailing.domain.common.DeviceIdentifier;
10
+import com.sap.sailing.domain.common.Position;
11
+import com.sap.sailing.domain.coursetemplate.CommonMarkProperties;
12
+import com.sap.sailing.domain.coursetemplate.CourseTemplate;
13
+import com.sap.sailing.domain.coursetemplate.FixedPositioning;
14
+import com.sap.sailing.domain.coursetemplate.MarkProperties;
15
+import com.sap.sailing.domain.coursetemplate.MarkRole;
16
+import com.sap.sailing.domain.coursetemplate.MarkTemplate;
17
+import com.sap.sailing.domain.coursetemplate.Positioning;
18
+import com.sap.sailing.domain.coursetemplate.RepeatablePart;
19
+import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
20
+import com.sap.sailing.shared.server.impl.ReplicatingSharedSailingData;
21
+import com.sap.sse.common.TimePoint;
22
+import com.sap.sse.common.impl.MillisecondsTimePoint;
23
+import com.sap.sse.replication.OperationWithResult;
24
+import com.sap.sse.replication.ReplicableWithObjectInputStream;
25
+import com.sap.sse.security.SecurityService;
26
+import com.sap.sse.security.shared.HasPermissions.DefaultActions;
27
+import com.sap.sse.security.shared.impl.UserGroup;
28
+
29
+/**
30
+ * {@link com.sap.sailing.server.interfaces.RacingEventService RacingEventService} is scoped to one server only. To
31
+ * share data that is not exclusively bound to one specific server but relevant for several servers,
32
+ * {@link SharedSailingData} provides an alternative replication scope. This means {@link SharedSailingData} may be
33
+ * replicated in a similar way as {@link SecurityService}.<br>
34
+ * {@link SharedSailingData} encapsulates persistence, replication and security aspects for the following domain types:
35
+ * <ul>
36
+ * <li>{@link MarkTemplate}s</li>
37
+ * <li>{@link MarkProperties}</li>
38
+ * <li>{@link CourseTemplate}s</li>
39
+ * </ul>
40
+ *
41
+ * In particular, the reading methods that produce iterables of entities, such as {@link #getAllMarkRoles()} or
42
+ * {@link #getAllMarkTemplates()}, will deliver a view restricted by the availability of the {@link DefaultActions#READ}
43
+ * permission of the user on whose behalf the request is being executed.
44
+ *
45
+ * @author Axel Uhl (D043530)
46
+ *
47
+ */
48
+public interface SharedSailingData extends ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> {
49
+
50
+ Iterable<MarkProperties> getAllMarkProperties(Iterable<String> tagsToFilterFor);
51
+
52
+ Iterable<MarkTemplate> getAllMarkTemplates();
53
+
54
+ Iterable<CourseTemplate> getAllCourseTemplates(Iterable<String> tagsToFilterFor);
55
+
56
+ Iterable<MarkRole> getAllMarkRoles();
57
+
58
+ MarkRole createMarkRole(String name, String shortName);
59
+
60
+ MarkRole getMarkRoleById(UUID id);
61
+
62
+ /**
63
+ * @param optionalNonDefaultGroupOwnership
64
+ * if {@link Optional#isPresent() present}, defines the {@link UserGroup} to use for the new mark
65
+ * properties object's group ownership. Otherwise, the session user's default creation group ownership
66
+ * will be used for the current server.
67
+ */
68
+ MarkProperties createMarkProperties(CommonMarkProperties properties, Iterable<String> tags, Optional<UserGroup> optionalNonDefaultGroupOwnership);
69
+
70
+ MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Iterable<String> tags);
71
+
72
+ /**
73
+ * @param positioningInformation
74
+ * if {@code null}, no update will be performed to the positioning information of the
75
+ * {@link MarkProperties} object identified by {@code uuid}. To clear the positioning information, pass
76
+ * in a valid, non-{@code null} {@link Positioning} object that makes an "empty" specification, such as a
77
+ * {@link FixedPositioning} with a {@code null} {@link FixedPositioning#getFixedPosition()} return.
78
+ */
79
+ MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Positioning positioningInformation, Iterable<String> tags);
80
+
81
+ /**
82
+ * This overrides a previously set fixed position or associated tracking device.
83
+ */
84
+ void setFixedPositionForMarkProperties(MarkProperties markProperties, Position position);
85
+
86
+ MarkProperties getMarkPropertiesById(UUID id);
87
+
88
+ /**
89
+ * This overrides a previously set fixed position or associated tracking device.
90
+ */
91
+ void setTrackingDeviceIdentifierForMarkProperties(MarkProperties markProperties, DeviceIdentifier deviceIdentifier);
92
+
93
+ MarkTemplate createMarkTemplate(CommonMarkProperties properties);
94
+
95
+ MarkTemplate getMarkTemplateById(UUID id);
96
+
97
+ /**
98
+ * @param waypoints the waypoints in their defined order (iteration order equals order of waypoints in course)
99
+ */
100
+ CourseTemplate createCourseTemplate(String courseTemplateName, String courseTemplateShortName,
101
+ Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints,
102
+ Map<MarkTemplate, MarkRole> associatedRoles, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles,
103
+ RepeatablePart optionalRepeatablePart, Iterable<String> tags, URL optionalImageURL, Integer defaultNumberOfLaps);
104
+
105
+ CourseTemplate getCourseTemplateById(UUID id);
106
+
107
+ /**
108
+ * Records the fact that the {@code markProperties} were used to configure a mark based on a
109
+ * {@code markTemplate}. Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be
110
+ * returned for {@code markTemplate} when invoking {@link #getUsedMarkProperties(MarkTemplate)}.
111
+ */
112
+ void recordUsage(MarkTemplate markTemplate, MarkProperties markProperties);
113
+
114
+ /**
115
+ * Records the fact that the {@code markProperties} were used to configure a mark that takes the given role name.
116
+ * Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be returned for the role name
117
+ * when invoking {@link #getUsedMarkProperties(String)}.
118
+ */
119
+ void recordUsage(MarkProperties markProperties, MarkRole markRole);
120
+
121
+ /**
122
+ * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkTemplate, MarkProperties) last used}
123
+ * for the {@link MarkTemplate} passed in the {@code markTemplate} parameter.
124
+ */
125
+ Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkTemplate markTemplate);
126
+
127
+ /**
128
+ * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkProperties, String) last used}
129
+ * for the role name passed in the {@code roleName} parameter.
130
+ */
131
+ Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkRole roleName);
132
+
133
+ void deleteMarkProperties(MarkProperties markProperties);
134
+
135
+ void deleteCourseTemplate(CourseTemplate courseTemplate);
136
+
137
+ Iterable<MarkProperties> getAllMarkProperties();
138
+
139
+ Iterable<CourseTemplate> getAllCourseTemplates();
140
+
141
+ CourseTemplate updateCourseTemplate(UUID uuid, String name, String shortName, URL optionalImageURL, ArrayList<String> tags,
142
+ Integer defaultNumberOfLaps);
143
+}
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java
0 144
index 9d3c0a3..06aa671
1
--- a/java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java
145
+++ b/java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java
... ...
@@ -1,17 +1,20 @@
1 1
package com.sap.sailing.shared.server.impl;
2 2
3
+import java.util.Dictionary;
3 4
import java.util.HashSet;
5
+import java.util.Hashtable;
4 6
import java.util.Set;
5 7
6 8
import org.osgi.framework.BundleActivator;
7 9
import org.osgi.framework.BundleContext;
8 10
import org.osgi.framework.ServiceRegistration;
9
-import org.osgi.util.tracker.ServiceTracker;
10 11
11
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
12 12
import com.sap.sailing.shared.persistence.PersistenceFactory;
13
+import com.sap.sailing.shared.server.SharedSailingData;
13 14
import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory;
15
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
14 16
import com.sap.sse.replication.Replicable;
17
+import com.sap.sse.replication.ReplicationService;
15 18
import com.sap.sse.security.SecurityService;
16 19
import com.sap.sse.util.ClearStateTestSupport;
17 20
import com.sap.sse.util.ServiceTrackerFactory;
... ...
@@ -24,21 +27,27 @@ public class Activator implements BundleActivator {
24 27
25 28
private Set<ServiceRegistration<?>> registrations = new HashSet<>();
26 29
27
- private ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
30
+ private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
28 31
29 32
private SharedSailingDataImpl sharedSailingData;
30 33
31 34
public void start(BundleContext context) throws Exception {
32 35
Activator.context = context;
33
- securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class);
36
+ securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class,
37
+ /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class));
38
+ securityServiceTracker.open();
34 39
serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context);
35 40
sharedSailingData = new SharedSailingDataImpl(PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(serviceFinderFactory),
36 41
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), serviceFinderFactory,
37 42
securityServiceTracker);
38 43
registrations.add(context.registerService(SharedSailingData.class, sharedSailingData, /* properties */ null));
44
+ registrations.add(context.registerService(ReplicatingSharedSailingData.class, sharedSailingData, /* properties */ null));
39 45
registrations
40 46
.add(context.registerService(ClearStateTestSupport.class, sharedSailingData, /* properties */ null));
41
- registrations.add(context.registerService(Replicable.class, sharedSailingData, /* properties */ null));
47
+ final Dictionary<String, String> replicableServiceProperties = new Hashtable<>();
48
+ replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name,
49
+ sharedSailingData.getId().toString());
50
+ registrations.add(context.registerService(Replicable.class, sharedSailingData, /* properties */ replicableServiceProperties));
42 51
}
43 52
44 53
public static BundleContext getContext() {
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/ReplicatingSharedSailingData.java
... ...
@@ -11,12 +11,9 @@ import com.sap.sailing.domain.coursetemplate.MarkTemplate;
11 11
import com.sap.sailing.domain.coursetemplate.Positioning;
12 12
import com.sap.sailing.domain.coursetemplate.RepeatablePart;
13 13
import com.sap.sailing.domain.coursetemplate.WaypointTemplate;
14
-import com.sap.sailing.domain.sharedsailingdata.SharedSailingData;
15
-import com.sap.sse.replication.OperationWithResult;
16
-import com.sap.sse.replication.ReplicableWithObjectInputStream;
14
+import com.sap.sailing.shared.server.SharedSailingData;
17 15
18
-public interface ReplicatingSharedSailingData extends SharedSailingData,
19
- ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> {
16
+public interface ReplicatingSharedSailingData extends SharedSailingData {
20 17
21 18
Void internalCreateMarkRole(UUID idOfNewMarkRole, String name, String shortName);
22 19
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/SharedSailingDataImpl.java
... ...
@@ -17,8 +17,6 @@ import java.util.UUID;
17 17
import java.util.concurrent.ConcurrentHashMap;
18 18
import java.util.stream.Collectors;
19 19
20
-import org.osgi.util.tracker.ServiceTracker;
21
-
22 20
import com.sap.sailing.domain.common.DeviceIdentifier;
23 21
import com.sap.sailing.domain.common.Position;
24 22
import com.sap.sailing.domain.common.security.SecuredDomainType;
... ...
@@ -47,6 +45,7 @@ import com.sap.sse.common.TypeBasedServiceFinder;
47 45
import com.sap.sse.common.TypeBasedServiceFinderFactory;
48 46
import com.sap.sse.common.Util;
49 47
import com.sap.sse.common.impl.MillisecondsTimePoint;
48
+import com.sap.sse.replication.FullyInitializedReplicableTracker;
50 49
import com.sap.sse.replication.OperationExecutionListener;
51 50
import com.sap.sse.replication.OperationWithResult;
52 51
import com.sap.sse.replication.OperationWithResultWithIdWrapper;
... ...
@@ -69,11 +68,11 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea
69 68
private final Map<UUID, MarkRole> markRolesById = new ConcurrentHashMap<>();
70 69
71 70
private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder;
72
- private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker;
71
+ private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker;
73 72
74 73
// Replication related methods and fields
75 74
private final ConcurrentHashMap<OperationExecutionListener<ReplicatingSharedSailingData>, OperationExecutionListener<ReplicatingSharedSailingData>> operationExecutionListeners = new ConcurrentHashMap<>();
76
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
75
+ private volatile boolean currentlyFillingFromInitialLoad;
77 76
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
78 77
private final Set<OperationWithResultWithIdWrapper<ReplicatingSharedSailingData, ?>> operationsSentToMasterForReplication = new HashSet<>();
79 78
private ReplicationMasterDescriptor master;
... ...
@@ -87,7 +86,8 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea
87 86
88 87
public SharedSailingDataImpl(final DomainObjectFactory domainObjectFactory,
89 88
final MongoObjectFactory mongoObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory,
90
- ServiceTracker<SecurityService, SecurityService> securityServiceTracker) {
89
+ FullyInitializedReplicableTracker<SecurityService> securityServiceTracker) {
90
+ this.currentlyFillingFromInitialLoad = false;
91 91
this.domainObjectFactory = domainObjectFactory;
92 92
this.mongoObjectFactory = mongoObjectFactory;
93 93
this.deviceIdentifierServiceFinder = serviceFinderFactory
... ...
@@ -123,7 +123,11 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea
123 123
}
124 124
125 125
public SecurityService getSecurityService() {
126
- return securityServiceTracker.getService();
126
+ try {
127
+ return securityServiceTracker.getInitializedService(0);
128
+ } catch (InterruptedException e) {
129
+ throw new RuntimeException(e);
130
+ }
127 131
}
128 132
129 133
@Override
... ...
@@ -579,12 +583,12 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea
579 583
580 584
@Override
581 585
public boolean isCurrentlyFillingFromInitialLoad() {
582
- return currentlyFillingFromInitialLoad.get();
586
+ return currentlyFillingFromInitialLoad;
583 587
}
584 588
585 589
@Override
586 590
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
587
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
591
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
588 592
}
589 593
590 594
@Override
java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/integration/AbstractReplicableWithObjectInputStream.java
... ...
@@ -45,7 +45,7 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera
45 45
46 46
private final Set<OperationWithResult<S, ?>> operationsSentToMasterForReplication;
47 47
48
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
48
+ private volatile boolean currentlyFillingFromInitialLoad;
49 49
50 50
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
51 51
... ...
@@ -54,6 +54,7 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera
54 54
public AbstractReplicableWithObjectInputStream() {
55 55
this.operationsSentToMasterForReplication = new HashSet<>();
56 56
this.operationExecutionListeners = new ConcurrentHashMap<>();
57
+ this.currentlyFillingFromInitialLoad = false;
57 58
}
58 59
59 60
@Override
... ...
@@ -86,12 +87,12 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera
86 87
87 88
@Override
88 89
public boolean isCurrentlyFillingFromInitialLoad() {
89
- return currentlyFillingFromInitialLoad.get();
90
+ return currentlyFillingFromInitialLoad;
90 91
}
91 92
92 93
@Override
93 94
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
94
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
95
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
95 96
}
96 97
97 98
@Override
java/com.sap.sailing.www/release_notes_admin.html
... ...
@@ -17,10 +17,10 @@
17 17
</div>
18 18
</div>
19 19
<div class="yield_content">
20
- <div class="contentWrapper">
21
- <div class="mainContent">
22
- <h2 class="releaseHeadline">Release Notes - Administration Console</h2>
23
- <div class="innerContent">
20
+ <div class="contentWrapper">
21
+ <div class="mainContent">
22
+ <h2 class="releaseHeadline">Release Notes - Administration Console</h2>
23
+ <div class="innerContent">
24 24
<h2 class="articleSubheadline">March 2020</h2>
25 25
<ul class="bulletList">
26 26
<li>White label: Removal of SAP logos and brand related text from home page and racing panels. Use system property <pre>-Dcom.sap.sailing.debranding=true</pre>
... ...
@@ -30,6 +30,12 @@
30 30
time allowances relevant for Performance Curve Scoring (PCS) and offers a link to the
31 31
official PDF version of the respective certificate, based on its reference number.
32 32
</li>
33
+ <li>Improved ESC/Enter key handling in user role/permission editing dialog</li>
34
+ <li>The "Replication" panel in the "Advanced" category now has improved information about the
35
+ replicables shown. An advanced replica information string may reveal more than just the address,
36
+ and the address is now resolved through any <tt>X-Forwarded-For</tt> header fields present
37
+ during replica registration, hence also working through load balancers and reverse proxies.
38
+ </li>
33 39
</ul>
34 40
<h2 class="articleSubheadline">February 2020</h2>
35 41
<ul class="bulletList">
java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java
... ...
@@ -48,7 +48,7 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa
48 48
private final Map<OperationExecutionListener<ReplicableFileStorageManagementService>, OperationExecutionListener<ReplicableFileStorageManagementService>> operationExecutionListeners = new ConcurrentHashMap<>();
49 49
private final Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication = new HashSet<>();
50 50
private ReplicationMasterDescriptor replicationMasterDescriptor;
51
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
51
+ private volatile boolean currentlyFillingFromInitialLoad;
52 52
53 53
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
54 54
... ...
@@ -68,6 +68,7 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa
68 68
69 69
public FileStorageManagementServiceImpl(TypeBasedServiceFinder<FileStorageService> serviceFinder,
70 70
FileStorageServicePropertyStore propertyStore) {
71
+ this.currentlyFillingFromInitialLoad = false;
71 72
this.serviceFinder = serviceFinder;
72 73
this.propertyStore = propertyStore;
73 74
serviceResolver = new FileStorageServiceResolverAgainstOsgiRegistryImpl(serviceFinder);
... ...
@@ -229,12 +230,12 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa
229 230
230 231
@Override
231 232
public boolean isCurrentlyFillingFromInitialLoad() {
232
- return currentlyFillingFromInitialLoad.get();
233
+ return currentlyFillingFromInitialLoad;
233 234
}
234 235
235 236
@Override
236 237
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
237
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
238
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
238 239
}
239 240
240 241
@Override
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java
... ...
@@ -81,10 +81,13 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd
81 81
protected final MultiWordSuggestOracle inputOracle;
82 82
protected final String placeholderTextForAddTextbox;
83 83
protected final Integer inputBoxSize;
84
+ private Button addButton;
85
+ private SuggestBox suggestBox;
86
+
84 87
public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable<String> suggestValues) {
85 88
this(stringMessages, removeImage, suggestValues, /* placeholderTextForAddTextbox */ null);
86 89
}
87
-
90
+
88 91
/**
89 92
* @param suggestValues must not be null but may be empty
90 93
* @param placeholderTextForAddTextbox may be null
... ...
@@ -94,6 +97,15 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd
94 97
this(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox, /* inputBoxSize */ null);
95 98
96 99
}
100
+
101
+ public void setEnabled(boolean enabled) {
102
+ // #5059 only disabled mode, enabling of button will be handled by internal logic of this component
103
+ if (!enabled) {
104
+ this.addButton.setEnabled(false);
105
+ }
106
+ this.suggestBox.setEnabled(enabled);
107
+ }
108
+
97 109
/**
98 110
* @param inputBoxSize The size of the input box in EM Unit.
99 111
*/
... ...
@@ -124,11 +136,11 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd
124 136
}
125 137
126 138
protected SuggestBox createSuggestBox() {
127
- final SuggestBox result = new SuggestBox(inputOracle);
139
+ suggestBox = new SuggestBox(inputOracle);
128 140
if (placeholderTextForAddTextbox != null) {
129
- result.getElement().setAttribute("placeholder", placeholderTextForAddTextbox);
141
+ suggestBox.getElement().setAttribute("placeholder", placeholderTextForAddTextbox);
130 142
}
131
- return result;
143
+ return suggestBox;
132 144
}
133 145
134 146
@Override
... ...
@@ -138,7 +150,7 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd
138 150
if (inputBoxSize != null) {
139 151
inputBox.setWidth(Integer.toString(inputBoxSize) + Unit.EM);
140 152
}
141
- final Button addButton = new Button(getStringMessages().add());
153
+ addButton = new Button(getStringMessages().add());
142 154
addButton.ensureDebugId("AddButton");
143 155
addButton.setEnabled(false);
144 156
addButton.addClickHandler(new ClickHandler() {
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/StringListEditorComposite.java
... ...
@@ -65,7 +65,9 @@ public class StringListEditorComposite extends GenericStringListEditorComposite<
65 65
}
66 66
67 67
public void setEnabled(final boolean enabled) {
68
- // is always of type CollapsedUi (see constructor)
69
- ((CollapsedUi) activeUi).setEnabled(enabled);
68
+ if (activeUi instanceof CollapsedUi)
69
+ ((CollapsedUi) activeUi).setEnabled(enabled);
70
+ if (activeUi instanceof ExpandedUi)
71
+ ((ExpandedUi<?>) activeUi).setEnabled(enabled);
70 72
}
71 73
}
java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java
... ...
@@ -48,7 +48,7 @@ public class MailServiceImpl implements ReplicableMailService {
48 48
private ReplicationMasterDescriptor replicatingFromMaster;
49 49
private final ConcurrentMap<OperationExecutionListener<ReplicableMailService>, OperationExecutionListener<ReplicableMailService>> operationExecutionListeners;
50 50
private final Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication = new HashSet<>();
51
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
51
+ private volatile boolean currentlyFillingFromInitialLoad;
52 52
53 53
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
54 54
... ...
@@ -63,6 +63,7 @@ public class MailServiceImpl implements ReplicableMailService {
63 63
private OperationsToMasterSendingQueue unsentOperationForMasterQueue;
64 64
65 65
public MailServiceImpl(Properties mailProperties, MailServiceResolver mailServiceResolver) {
66
+ this.currentlyFillingFromInitialLoad = false;
66 67
this.mailProperties = mailProperties;
67 68
this.operationExecutionListeners = new ConcurrentHashMap<>();
68 69
this.mailServiceResolver = mailServiceResolver;
... ...
@@ -238,12 +239,12 @@ public class MailServiceImpl implements ReplicableMailService {
238 239
239 240
@Override
240 241
public boolean isCurrentlyFillingFromInitialLoad() {
241
- return currentlyFillingFromInitialLoad.get();
242
+ return currentlyFillingFromInitialLoad;
242 243
}
243 244
244 245
@Override
245 246
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
246
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
247
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
247 248
}
248 249
249 250
@Override
java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF
... ...
@@ -9,7 +9,9 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
9 9
Export-Package: com.sap.sse.replication,
10 10
com.sap.sse.replication.impl
11 11
Import-Package: com.rabbitmq.client;version="2.8.4",
12
- org.json.simple
12
+ org.json.simple,
13
+ org.osgi.framework;version="1.8.0",
14
+ org.osgi.util.tracker;version="1.5.1"
13 15
Require-Bundle: com.sap.sse.operationaltransformation;bundle-version="1.0.0",
14 16
com.sap.sse.common;bundle-version="1.0.0",
15 17
com.sap.sse;bundle-version="1.0.0"
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/FullyInitializedReplicableTracker.java
... ...
@@ -0,0 +1,134 @@
1
+package com.sap.sse.replication;
2
+
3
+import java.util.concurrent.CountDownLatch;
4
+import java.util.concurrent.TimeUnit;
5
+
6
+import org.osgi.framework.BundleContext;
7
+import org.osgi.framework.Filter;
8
+import org.osgi.framework.ServiceReference;
9
+import org.osgi.util.tracker.ServiceTracker;
10
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
11
+
12
+import com.sap.sse.replication.ReplicationService.ReplicationStartingListener;
13
+
14
+/**
15
+ * While a regular OSGi {@link ServiceTracker} would {@link ServiceTracker#waitForService(long) wait} for the service's
16
+ * appearance in the OSGi registry and then return it, this specialization is aware of the {@link ReplicationService}
17
+ * and understands the replication life cycle which can be in {@link ReplicationService#isReplicationStarting()
18
+ * starting} mode, and furthermore each replicable can be in the process of handling its initial load. This tracker has
19
+ * a specialized {@link #waitForService(long)} implementation that waits for the {@link ReplicationService} to not be in
20
+ * state {@link ReplicationService#isReplicationStarting()}, waits for the replicable requested using the regular
21
+ * (super-class) {@link ServiceTracker#waitForService(long)} method and asserts the {@link Replicable} to return is
22
+ * not in the state {@link Replicable#isCurrentlyFillingFromInitialLoad()}.<p>
23
+ *
24
+ * By analogy, the {@link #getService()} and related methods return only services for which the above rules apply.
25
+ * In particular, {@link #getService()} will return {@code null} if the {@link ReplicationService} is currently in mode
26
+ * {@link ReplicationService#isReplicationStarting()} or the replicable found by the regular OSGi tracker is currently
27
+ * {@link Replicable#isCurrentlyFillingFromInitialLoad() receiving its initial load}.<p>
28
+ *
29
+ * Note that {@link #waitForService(long)} must not be used during the activation of a {@link Replicable}'s bundle before
30
+ * that {@link Replicable} has been registered with the OSGi registry. Otherwise, a deadlock can result because the waiting
31
+ * {@link Replicable} will hold up the completion of the replication start-up and hence wait forever.
32
+ *
33
+ * @author Axel Uhl (D043530)
34
+ *
35
+ * @param <R>
36
+ * the type of {@link Replicable} to track
37
+ */
38
+public class FullyInitializedReplicableTracker<R extends Replicable<?, ?>> extends ServiceTracker<R, R> {
39
+ /**
40
+ * Used to find the {@link ReplicationService}. If {@code null}, no attempt will be made to look
41
+ * for the {@link ReplicationService}, and only the replicable's {@link Replicable#isCurrentlyFillingFromInitialLoad()}
42
+ * result will be considered.
43
+ */
44
+ private final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker;
45
+
46
+ public FullyInitializedReplicableTracker(BundleContext context, Class<R> clazz,
47
+ ServiceTrackerCustomizer<R, R> customizer,
48
+ ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) {
49
+ super(context, clazz, customizer);
50
+ this.replicationServiceTracker = replicationServiceTracker;
51
+ }
52
+
53
+ public FullyInitializedReplicableTracker(BundleContext context, Filter filter,
54
+ ServiceTrackerCustomizer<R, R> customizer,
55
+ ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) {
56
+ super(context, filter, customizer);
57
+ this.replicationServiceTracker = replicationServiceTracker;
58
+ }
59
+
60
+ public FullyInitializedReplicableTracker(BundleContext context, ServiceReference<R> reference,
61
+ ServiceTrackerCustomizer<R, R> customizer,
62
+ ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) {
63
+ super(context, reference, customizer);
64
+ this.replicationServiceTracker = replicationServiceTracker;
65
+ }
66
+
67
+ public FullyInitializedReplicableTracker(BundleContext context, String clazz,
68
+ ServiceTrackerCustomizer<R, R> customizer,
69
+ ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) {
70
+ super(context, clazz, customizer);
71
+ this.replicationServiceTracker = replicationServiceTracker;
72
+ }
73
+
74
+ /**
75
+ * @param timeoutInMillis
76
+ * 0 means indefinite waiting time
77
+ * @return {@code true} if the {@link #replicationServiceTracker} is {@code null} or the {@link ReplicationService}
78
+ * was obtained successfully and has been in or has reached the state of not
79
+ * {@link ReplicationService#isReplicationStarting()} within the timeout provided. {@code false} otherwise.
80
+ */
81
+ private boolean waitForReplicationToBeInitialized(long timeoutInMillis) throws InterruptedException {
82
+ final boolean result;
83
+ if (replicationServiceTracker != null) {
84
+ final CountDownLatch latch = new CountDownLatch(1); // counted down either by the direct check or the listener
85
+ final ReplicationService replicationService = replicationServiceTracker.waitForService(timeoutInMillis);
86
+ final ReplicationStartingListener replicationStartingListener = newIsReplicationStarting->{
87
+ if (!newIsReplicationStarting) {
88
+ latch.countDown();
89
+ }
90
+ };
91
+ replicationService.addReplicationStartingListener(replicationStartingListener);
92
+ if (!replicationService.isReplicationStarting()) {
93
+ latch.countDown();
94
+ }
95
+ if (timeoutInMillis == 0) {
96
+ latch.await();
97
+ result = true;
98
+ } else {
99
+ result = latch.await(timeoutInMillis, TimeUnit.MILLISECONDS);
100
+ }
101
+ replicationService.removeReplicationStartingListener(replicationStartingListener);
102
+ } else {
103
+ result = true;
104
+ }
105
+ return result;
106
+ }
107
+
108
+ /**
109
+ * Waits for one service object tracked to appear for {@code timeoutInMillis} milliseconds (see
110
+ * {@link #waitForService(long)}). If no such service object can be found before timing out, {@code null}
111
+ * is returned. Once a service object has been retrieved and a non-{@code null} {@link #replicationServiceTracker}
112
+ * has been provided at construction time, the {@link ReplicationService} is obtained from that tracker by
113
+ * waiting for it at least {@code timeoutInMillis} milliseconds and then
114
+ *
115
+ * @param timeoutInMillis
116
+ * 0 means indefinite wait time
117
+ * @return {@code null} if no service was obtained from the registry in the timeout specified or the replication did
118
+ * not reach a fully initialized state in the timeout specified.
119
+ */
120
+ public R getInitializedService(long timeoutInMillis) throws InterruptedException {
121
+ final R service = waitForService(timeoutInMillis);
122
+ final R result;
123
+ if (service != null) {
124
+ if (waitForReplicationToBeInitialized(timeoutInMillis)) {
125
+ result = service;
126
+ } else {
127
+ result = null;
128
+ }
129
+ } else {
130
+ result = null;
131
+ }
132
+ return result;
133
+ }
134
+}
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationWithResult.java
... ...
@@ -45,6 +45,13 @@ public interface OperationWithResult<S, R> extends Operation<S>, Serializable {
45 45
default boolean isRequiresExplicitTransitiveReplication() {
46 46
return true;
47 47
}
48
+
49
+ /**
50
+ * @return the class to use for keeping statistics; this allows wrapper classes to report the actual inner class
51
+ */
52
+ default Class<?> getClassForLogging() {
53
+ return getClass();
54
+ }
48 55
49 56
/**
50 57
* Ignores the actual result of {@link #internalApplyTo(Object)} and returns <code>toState</code> which
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationWithResultWithIdWrapper.java
... ...
@@ -24,8 +24,7 @@ public class OperationWithResultWithIdWrapper<S, R> implements OperationWithResu
24 24
* Creates a new UUID for this wrapper operation
25 25
*/
26 26
public OperationWithResultWithIdWrapper(OperationWithResult<S, R> delegate) {
27
- this.delegate = delegate;
28
- this.id = UUID.randomUUID();
27
+ this(delegate, UUID.randomUUID());
29 28
}
30 29
31 30
/**
... ...
@@ -37,6 +36,14 @@ public class OperationWithResultWithIdWrapper<S, R> implements OperationWithResu
37 36
this.delegate = delegate;
38 37
}
39 38
39
+ /**
40
+ * @return the {@link #delegate}'s class for keeping statistics
41
+ */
42
+ @Override
43
+ public Class<?> getClassForLogging() {
44
+ return delegate.getClass();
45
+ }
46
+
40 47
@Override
41 48
public int hashCode() {
42 49
final int prime = 31;
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicationService.java
... ...
@@ -113,7 +113,20 @@ public interface ReplicationService {
113 113
*/
114 114
ReplicationReceiver getReplicator();
115 115
116
+ /**
117
+ * An update to this attribute can be observed by registering a {@link ReplicationStartingListener} using
118
+ * the {@link #addReplicationStartingListener} method.
119
+ */
116 120
void setReplicationStarting(boolean b);
121
+
122
+ @FunctionalInterface
123
+ public static interface ReplicationStartingListener {
124
+ void onReplicationStartingChanged(boolean newReplicationStartingValue);
125
+ }
126
+
127
+ void addReplicationStartingListener(ReplicationStartingListener listener);
128
+
129
+ void removeReplicationStartingListener(ReplicationStartingListener listener);
117 130
118 131
/**
119 132
* @return {@code true} if replication is starting; during this phase it is clear that the replicables managed by
java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java
120 133
index 47e4484..ccdf4e0
121
--- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java
134
+++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java
... ...
@@ -175,6 +175,7 @@ public class Activator implements BundleActivator {
175 175
" before firing up replication automatically...");
176 176
final List<Replicable<?, ?>> replicables = new ArrayList<>();
177 177
for (String replicableIdAsString : replicableIdsAsStrings) {
178
+ logger.info("Trying to obtain Replicable " + replicableIdAsString+", waiting if necessary");
178 179
Replicable<?, ?> replicable = replicablesProvider.getReplicable(replicableIdAsString, /* wait */true);
179 180
logger.info("Obtained Replicable " + replicableIdAsString);
180 181
replicables.add(replicable);
java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java
... ...
@@ -19,6 +19,7 @@ import java.util.List;
19 19
import java.util.Map;
20 20
import java.util.Optional;
21 21
import java.util.Random;
22
+import java.util.Set;
22 23
import java.util.Timer;
23 24
import java.util.TimerTask;
24 25
import java.util.UUID;
... ...
@@ -232,7 +233,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
232 233
/**
233 234
* Will be set
234 235
*/
235
- private boolean replicationStarting;
236
+ private volatile boolean replicationStarting;
236 237
237 238
/**
238 239
* An optional link to a persistence layer that allows this service to record replicas
... ...
@@ -240,6 +241,8 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
240 241
* links can be re-established.
241 242
*/
242 243
private final Optional<MongoObjectFactory> mongoObjectFactory;
244
+
245
+ private final Set<ReplicationStartingListener> replicationStartingListeners;
243 246
244 247
private static class InitialLoadRequest {
245 248
private final Channel channelForInitialLoad;
... ...
@@ -320,6 +323,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
320 323
Optional<MongoObjectFactory> optionalMongoObjectFactory, String exchangeName, String exchangeHost, int exchangePort,
321 324
ReplicationInstancesManager replicationInstancesManager, ReplicablesProvider replicablesProvider) throws IOException {
322 325
this.mongoObjectFactory = optionalMongoObjectFactory;
326
+ this.replicationStartingListeners = new HashSet<>();
323 327
timer = new Timer("ReplicationServiceImpl timer for delayed task sending", /* isDaemon */ true);
324 328
unsentOperationsSenderJob = new UnsentOperationsSenderJob();
325 329
executionListenersByReplicableIdAsString = new ConcurrentHashMap<>();
... ...
@@ -521,7 +525,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
521 525
outboundBufferClasses = new ArrayList<>();
522 526
}
523 527
outboundObjectBuffer.writeObject(serializedOperation);
524
- outboundBufferClasses.add(operation.getClass());
528
+ outboundBufferClasses.add(operation.getClassForLogging());
525 529
if (outboundBuffer.size() > TRIGGER_MESSAGE_SIZE_IN_BYTES) {
526 530
logger.info("Triggering replication because buffer holds " + outboundBuffer.size()
527 531
+ " bytes which exceeds trigger size " + TRIGGER_MESSAGE_SIZE_IN_BYTES + " bytes");
... ...
@@ -647,7 +651,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
647 651
final Iterable<Replicable<?, ?>> replicables = master.getReplicables();
648 652
logger.info("Starting to replicate from " + master);
649 653
try {
650
- registerReplicaWithMaster(master, replicables);
654
+ registerReplicaWithMaster(master);
651 655
} catch (Exception ex) {
652 656
logger.log(Level.SEVERE, "ERROR", ex);
653 657
throw ex;
... ...
@@ -680,7 +684,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
680 684
final URL initialLoadURL = master.getInitialLoadURL(replicables);
681 685
logger.info("Initial load URL is " + initialLoadURL);
682 686
// start receiving messages already now, but start in suspended mode
683
- replicator = new ReplicationReceiverImpl(master, replicablesProvider, /* startSuspended */true, consumer);
687
+ replicator = new ReplicationReceiverImpl(master, replicablesProvider, /* startSuspended */ true, consumer);
684 688
// clear Replicable state here, before starting to receive and de-serialize operations which builds up
685 689
// new state, e.g., in competitor store
686 690
for (Replicable<?, ?> r : replicables) {
... ...
@@ -733,18 +737,13 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
733 737
}
734 738
735 739
/**
736
- * @param replicables
737
- * the replica is registered for these {@link Replicable}s. The master sends operations only for
738
- * replicables that at least one replica has registered for. This may mean that operations are received
739
- * for replicables for which no replicable was requested. Replicas shall drop such operations silently.
740
- * TODO unused...
741
- *
742 740
* @return the UUID that the master generated for this client which is also entered into {@link #replicaUUIDs}
743 741
*/
744
- private String registerReplicaWithMaster(ReplicationMasterDescriptor master, Iterable<Replicable<?, ?>> replicables) throws IOException,
742
+ private String registerReplicaWithMaster(ReplicationMasterDescriptor master) throws IOException,
745 743
ClassNotFoundException {
746 744
URL replicationRegistrationRequestURL = master.getReplicationRegistrationRequestURL(getServerIdentifier(),
747 745
ServerInfo.getBuildVersion());
746
+ logger.info("Replication registration request URL: "+replicationRegistrationRequestURL);
748 747
final URLConnection registrationRequestConnection = HttpUrlConnectionHelper
749 748
.redirectConnectionWithBearerToken(replicationRegistrationRequestURL, master.getBearerToken());
750 749
final InputStream content = (InputStream) registrationRequestConnection.getContent();
... ...
@@ -761,6 +760,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
761 760
}
762 761
}
763 762
final String replicaUUID = uuid.toString();
763
+ logger.info("Obtained replica UUID "+replicaUUID+" from master");
764 764
registerReplicaUuidForMaster(replicaUUID, master);
765 765
return replicaUUID;
766 766
}
... ...
@@ -891,9 +891,31 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM
891 891
servletPort, bearerToken, replicables);
892 892
}
893 893
894
+
895
+ @Override
896
+ public void addReplicationStartingListener(ReplicationStartingListener listener) {
897
+ synchronized (replicationStartingListeners) {
898
+ replicationStartingListeners.add(listener);
899
+ }
900
+ }
901
+
894 902
@Override
895
- public void setReplicationStarting(boolean b) {
896
- this.replicationStarting = b;
903
+ public void removeReplicationStartingListener(ReplicationStartingListener listener) {
904
+ synchronized (replicationStartingListeners) {
905
+ replicationStartingListeners.remove(listener);
906
+ }
907
+ }
908
+
909
+ @Override
910
+ public void setReplicationStarting(boolean newReplicationStarting) {
911
+ if (this.replicationStarting != newReplicationStarting) {
912
+ this.replicationStarting = newReplicationStarting;
913
+ synchronized (replicationStartingListeners) {
914
+ for (final ReplicationStartingListener listener : replicationStartingListeners) {
915
+ listener.onReplicationStartingChanged(newReplicationStarting);
916
+ }
917
+ }
918
+ }
897 919
}
898 920
899 921
@Override
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PermissionChecker.java
... ...
@@ -166,8 +166,9 @@ public class PermissionChecker {
166 166
* permission. This is fine when checking for a specific permission like USER:UPDATE:my_user. If one user grants a
167 167
* meta permission like USER:*:* for another user this would mean the current user needs an equivalent meta
168 168
* permission or one that is even more general like *:*:* or USER,USER_GROUP:*:*. In contrast to that, a user who
169
- * owns the permissions USER:*:* and USER_GROUP:*:* may not grant USER,USER_GROUP:*:* because this meta permission
170
- * is not implied by a single permission held by the current user.<br>
169
+ * owns the permissions USER:*:* and USER_GROUP:*:* would---with the <em>regular</em> permission check---not be able
170
+ * to grant USER,USER_GROUP:*:* because this meta permission would not be implied by a single permission held by the
171
+ * current user.<br>
171 172
* To solve this we use the given {@link HasPermissions} instances to resolve wildcards for the type and action
172 173
* parts and construct distinct permissions to check. This means the given {@link HasPermissions} need to be a
173 174
* complete list for the running system. Otherwise we potentially do not check for all required types if the given
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/Role.java
... ...
@@ -15,10 +15,12 @@ public class Role extends AbstractRole<RoleDefinition, UserGroup, User> {
15 15
}
16 16
17 17
public Ownership getQualificationAsOwnership() {
18
+ final Ownership result;
18 19
if (qualifiedForTenant != null || qualifiedForUser != null) {
19
- return new Ownership(qualifiedForUser, qualifiedForTenant);
20
+ result = new Ownership(qualifiedForUser, qualifiedForTenant);
21
+ } else {
22
+ result = null;
20 23
}
21
- return null;
24
+ return result;
22 25
}
23
-
24 26
}
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/UserGroupImpl.java
... ...
@@ -15,7 +15,7 @@ public class UserGroupImpl extends AbstractUserGroupImpl<User, RoleDefinition> i
15 15
private static final long serialVersionUID = -1290667868080992763L;
16 16
17 17
public UserGroupImpl(UUID id, String name) {
18
- super(id, name, new HashSet<User>(), new HashMap<RoleDefinition, Boolean>());
18
+ this(id, name, new HashSet<>(), new HashMap<>());
19 19
}
20 20
21 21
public UserGroupImpl(UUID id, String name, Set<User> users, Map<RoleDefinition, Boolean> roleDefinitionMap) {
java/com.sap.sse.security.storemerging/src/com/sap/sse/security/storemerging/SecurityStoreMerger.java
... ...
@@ -137,7 +137,7 @@ public class SecurityStoreMerger {
137 137
replaceSourceOwnershipReferencesToUsersAndGroups(sourceAccessControlStore, userMap, userGroupMap);
138 138
mergePreferences(sourceUserStore, userMap);
139 139
mergeOwnerships(sourceAccessControlStore, ownershipsToTryToImport);
140
- mergeAccessControlLists(sourceAccessControlStore, userGroupMap);
140
+ mergeAccessControlLists(sourceAccessControlStore);
141 141
}
142 142
143 143
private Map<User, User> markUsersForAddMergeOrDrop(UserStore sourceUserStore, AccessControlStore sourceAccessControlStore) {
... ...
@@ -543,7 +543,11 @@ public class SecurityStoreMerger {
543 543
}
544 544
}
545 545
546
- private void mergeAccessControlLists(AccessControlStore sourceAccessControlStore, Map<UserGroup, UserGroup> userGroupMap) {
546
+ /**
547
+ * The source access control lists are expected to have their groups already updated to point to their corresponding
548
+ * groups in the target store. See also {@link #replaceSourceAccessControlListReferencesToGroups(AccessControlStore, Map)}.
549
+ */
550
+ private void mergeAccessControlLists(AccessControlStore sourceAccessControlStore) {
547 551
logger.info("Applying all source ACLs to target");
548 552
for (final AccessControlListAnnotation sourceACL : sourceAccessControlStore.getAccessControlLists()) {
549 553
for (final Entry<UserGroup, Set<String>> permissionsPerGroup : sourceACL.getAnnotation().getActionsByUserGroup().entrySet()) {
java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
... ...
@@ -8,10 +8,17 @@ import static org.junit.Assert.assertSame;
8 8
import static org.junit.Assert.assertTrue;
9 9
10 10
import java.net.UnknownHostException;
11
+import java.util.Arrays;
11 12
import java.util.Collections;
13
+import java.util.HashMap;
14
+import java.util.HashSet;
15
+import java.util.Map;
16
+import java.util.Set;
12 17
import java.util.UUID;
13 18
14 19
import org.apache.shiro.SecurityUtils;
20
+import org.hamcrest.Matchers;
21
+import org.junit.Assert;
15 22
import org.junit.Before;
16 23
import org.junit.Test;
17 24
... ...
@@ -30,6 +37,8 @@ import com.sap.sse.security.shared.RoleDefinition;
30 37
import com.sap.sse.security.shared.UserGroupManagementException;
31 38
import com.sap.sse.security.shared.UserManagementException;
32 39
import com.sap.sse.security.shared.WildcardPermission;
40
+import com.sap.sse.security.shared.impl.AccessControlList;
41
+import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
33 42
import com.sap.sse.security.shared.impl.Role;
34 43
import com.sap.sse.security.shared.impl.User;
35 44
import com.sap.sse.security.shared.impl.UserGroup;
... ...
@@ -84,6 +93,25 @@ public class LoginTest {
84 93
assertFalse(Util.contains(specialUserGroup2.getUsers(), user));
85 94
}
86 95
96
+
97
+ @Test
98
+ public void testAclAnonUserGroup() throws UserManagementException, MailException, UserGroupManagementException {
99
+ final String username = "TheNewUser";
100
+ securityService.createSimpleUser(username, "u@a.b", "Humba", username, /* company */ null,
101
+ /* locale */ null, /* validationBaseURL */ null, /* owning group */ null);
102
+ final UserGroup defaultUserGroup = securityService.getUserGroupByName(username + SecurityService.TENANT_SUFFIX);
103
+ Map<UserGroup, Set<String>> permissionMap = new HashMap<>();
104
+ permissionMap.put(defaultUserGroup, new HashSet<>(Arrays.asList(new String[] { "!READ", "UPDATE" })));
105
+ permissionMap.put(null, new HashSet<>(Arrays.asList(new String[] { "!READ", "UPDATE" })));
106
+ AccessControlList acl = securityService.updateAccessControlList(
107
+ QualifiedObjectIdentifierImpl.fromDBWithoutEscaping("someid/more"), permissionMap);
108
+
109
+ Map<UserGroup, Set<String>> result = acl.getActionsByUserGroup();
110
+
111
+ Assert.assertThat(result.get(defaultUserGroup), Matchers.contains("!READ", "UPDATE"));
112
+ Assert.assertThat(result.get(null), Matchers.contains("UPDATE"));
113
+ }
114
+
87 115
@Test
88 116
public void testGetUser() {
89 117
assertNotNull("Subject should not be null: ", SecurityUtils.getSubject());
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/editacl/AclEditPanel.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sse.security.ui.client.component.editacl;
2 2
3 3
import static com.sap.sse.gwt.client.Notification.NotificationType.ERROR;
4 4
import static com.sap.sse.gwt.client.Notification.NotificationType.SUCCESS;
5
+import static com.sap.sse.gwt.shared.DebugConstants.DEBUG_ID_ATTRIBUTE;
5 6
6 7
import java.util.ArrayList;
7 8
import java.util.Collection;
... ...
@@ -57,7 +58,7 @@ import com.sap.sse.security.ui.client.i18n.StringMessages;
57 58
public class AclEditPanel extends Composite {
58 59
59 60
private static AclEditPanelUiBinder uiBinder = GWT.create(AclEditPanelUiBinder.class);
60
-
61
+
61 62
interface AclEditPanelUiBinder extends UiBinder<Widget, AclEditPanel> {
62 63
}
63 64
... ...
@@ -104,12 +105,14 @@ public class AclEditPanel extends Composite {
104 105
initWidget(uiBinder.createAndBindUi(this));
105 106
final CellList<StrippedUserGroupDTO> userGroupList = createUserGroupCellList();
106 107
userGroupDataProvider.addDataDisplay(userGroupList);
108
+
107 109
userGroupCellListPanelUi.add(wrapIntoCaptionPanel(userGroupList, stringMessages.userGroups(),
108 110
suggestUserGroupUi, addUserGroupButtonUi, removeUserGroupButtonUi));
109 111
110 112
// retrieve set of available action names
111 113
final List<String> actionNames = Stream.of(availableActions).map(Action::name).collect(Collectors.toList());
112 114
115
+
113 116
// create action editor for allowed actions
114 117
allowedActionsEditor = new StringListEditorComposite(new ArrayList<>(), stringMessages,
115 118
IconResources.INSTANCE.removeIcon(), actionNames, stringMessages.allowedActionName());
... ...
@@ -126,8 +129,18 @@ public class AclEditPanel extends Composite {
126 129
permissionsCellListPanelUi.add(deniedActionsContainer = createActionsContainer(stringMessages.deniedActions(),
127 130
deniedActionsEditor, AclDialogResources.INSTANCE.css().deniedActionsTable()));
128 131
132
+ userGroupSelectionModel.addSelectionChangeHandler(event -> {
133
+ StrippedUserGroupDTO userGroup = userGroupSelectionModel.getSelectedObject();
134
+ deniedActionsEditor.setEnabled(userGroup != null && userGroup.getId() != null);
135
+ });
136
+
129 137
lblId.setText(id);
130 138
lblType.setText(typeIdentifier);
139
+
140
+ addUserGroupButtonUi.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "AddUserGroupButton");
141
+ suggestUserGroupUi.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "SuggestUserGroupInput");
142
+ allowedActionsContainer.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "allowedActionsContainer");
143
+ deniedActionsContainer.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "deniedActionsContainer");
131 144
}
132 145
133 146
private CaptionPanel createActionsContainer(final SafeHtml caption, final Widget content, final String styleName) {
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/editacl/EditACLDialog.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sse.security.ui.client.component.editacl;
2 2
3 3
import static com.sap.sse.gwt.client.Notification.NotificationType.ERROR;
4
+import static com.sap.sse.gwt.shared.DebugConstants.DEBUG_ID_ATTRIBUTE;
4 5
5 6
import java.util.function.Consumer;
6 7
import java.util.function.Function;
... ...
@@ -66,6 +67,8 @@ public class EditACLDialog extends DataEntryDialog<AclDialogResult> {
66 67
aclEditPanel.updateAcl(result);
67 68
}
68 69
});
70
+
71
+ super.getDialogBox().getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "AclDialog");
69 72
}
70 73
71 74
@Override
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/EditUserRolesAndPermissionsDialog.java
... ...
@@ -35,15 +35,13 @@ public class EditUserRolesAndPermissionsDialog extends DataEntryDialog<Void> {
35 35
super(stringMessages.editRolesAndPermissionsForUser(selectedUsername), null, stringMessages.close(), null,
36 36
/* validator */ null, /* animationEnabled */true, callback);
37 37
final MultiSelectionModel<UserDTO> selectionAdapter = new MultiSelectionModel<>();
38
- super.getCancelButton().removeFromParent();
39
-
38
+ super.getCancelButton().setVisible(false);
40 39
final Runnable updater = new Runnable() {
41 40
@Override
42 41
public void run() {
43 42
selectionAdapter.clear();
44 43
userService.getUserManagementService().getRolesAndPermissionsForUser(selectedUsername,
45 44
new AsyncCallback<RolesAndPermissionsForUserDTO>() {
46
-
47 45
@Override
48 46
public void onSuccess(RolesAndPermissionsForUserDTO result) {
49 47
selectionAdapter.clear();
... ...
@@ -60,12 +58,10 @@ public class EditUserRolesAndPermissionsDialog extends DataEntryDialog<Void> {
60 58
});
61 59
}
62 60
};
63
-
64 61
wildcardPermissionPanel = new WildcardPermissionPanel(userService, stringMessages, errorReporter,
65
- tableResources, selectionAdapter, updater);
62
+ tableResources, selectionAdapter, updater, oracle->createSuggestBox(oracle));
66 63
userRoleDefinitionPanel = new UserRoleDefinitionPanel(userService, stringMessages, errorReporter,
67
- tableResources, selectionAdapter, updater);
68
-
64
+ tableResources, selectionAdapter, updater, oracle->createSuggestBox(oracle), ()->createTextBox(""));
69 65
updater.run();
70 66
}
71 67
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserManagementPanel.java
... ...
@@ -16,6 +16,7 @@ import com.google.gwt.user.client.ui.Button;
16 16
import com.google.gwt.user.client.ui.DockPanel;
17 17
import com.google.gwt.user.client.ui.HorizontalPanel;
18 18
import com.google.gwt.user.client.ui.ScrollPanel;
19
+import com.google.gwt.user.client.ui.SuggestBox;
19 20
import com.google.gwt.user.client.ui.TextBox;
20 21
import com.google.gwt.user.client.ui.VerticalPanel;
21 22
import com.sap.sse.gwt.client.ErrorReporter;
... ...
@@ -136,11 +137,11 @@ public class UserManagementPanel<TR extends CellTableWithCheckboxResources> exte
136 137
// add details panel for user roles
137 138
userRoleDefinitionPanel = new UserRoleDefinitionPanel(userService, stringMessages,
138 139
errorReporter,
139
- tableResources, userList.getSelectionModel(), () -> updateUsers());
140
+ tableResources, userList.getSelectionModel(), () -> updateUsers(), SuggestBox::new, TextBox::new);
140 141
detailsPanel.add(userRoleDefinitionPanel);
141 142
// add details panel for user permissions
142 143
final WildcardPermissionPanel userPermissionPanel = new WildcardPermissionPanel(userService, stringMessages,
143
- errorReporter, tableResources, userList.getSelectionModel(), () -> updateUsers());
144
+ errorReporter, tableResources, userList.getSelectionModel(), () -> updateUsers(), SuggestBox::new);
144 145
detailsPanel.add(userPermissionPanel);
145 146
west.add(detailsPanel);
146 147
}
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionPanel.java
... ...
@@ -1,5 +1,7 @@
1 1
package com.sap.sse.security.ui.client.usermanagement.permissions;
2 2
3
+import java.util.function.Function;
4
+
3 5
import com.google.gwt.core.shared.GWT;
4 6
import com.google.gwt.event.dom.client.ChangeEvent;
5 7
import com.google.gwt.event.dom.client.ChangeHandler;
... ...
@@ -15,6 +17,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel;
15 17
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
16 18
import com.google.gwt.user.client.ui.ScrollPanel;
17 19
import com.google.gwt.user.client.ui.SuggestBox;
20
+import com.google.gwt.user.client.ui.SuggestOracle;
18 21
import com.google.gwt.user.client.ui.UIObject;
19 22
import com.google.gwt.user.client.ui.VerticalPanel;
20 23
import com.google.gwt.view.client.MultiSelectionModel;
... ...
@@ -49,8 +52,8 @@ public class WildcardPermissionPanel extends HorizontalPanel
49 52
50 53
public WildcardPermissionPanel(final UserService userService, final StringMessages stringMessages,
51 54
final ErrorReporter errorReporter, final CellTableWithCheckboxResources tableResources,
52
- final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers) {
53
-
55
+ final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers,
56
+ Function<SuggestOracle, SuggestBox> suggestBoxConstructor) {
54 57
// create multi to single selection adapter
55 58
final SingleSelectionModel<UserDTO> multiToSingleSelectionModelAdapter = new SingleSelectionModel<>();
56 59
userSelectionModel.addSelectionChangeHandler(event -> {
... ...
@@ -67,7 +70,6 @@ public class WildcardPermissionPanel extends HorizontalPanel
67 70
}
68 71
});
69 72
this.userSelectionModel = multiToSingleSelectionModelAdapter;
70
-
71 73
// create suggest for permission
72 74
final MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
73 75
for (HasPermissions permission : userService.getAllKnownPermissions()) {
... ...
@@ -75,17 +77,14 @@ public class WildcardPermissionPanel extends HorizontalPanel
75 77
oracle.add(permission.getStringPermission(action));
76 78
}
77 79
}
78
- suggestPermission = new SuggestBox(oracle);
80
+ suggestPermission = suggestBoxConstructor.apply(oracle);
79 81
roleAndPermissionResources.css().ensureInjected();
80 82
suggestPermission.addStyleName(roleAndPermissionResources.css().enterPermissionSuggest());
81 83
suggestPermission.getElement().setPropertyString("placeholder", stringMessages.enterPermissionName());
82 84
this.initPlaceholder(suggestPermission, stringMessages.enterPermissionName());
83
-
84 85
// create permission input panel + add controls
85 86
final HorizontalPanel permissionInputPanel = new HorizontalPanel();
86
-
87 87
permissionInputPanel.add(suggestPermission);
88
-
89 88
final Button addPermissionButton = new Button(stringMessages.add(), (ClickHandler) event -> {
90 89
WildcardPermission selectedPermission = new WildcardPermission(suggestPermission.getText());
91 90
if (selectedPermission != null) {
... ...
@@ -93,7 +92,6 @@ public class WildcardPermissionPanel extends HorizontalPanel
93 92
if (selectedUser != null) {
94 93
userService.getUserManagementService().addPermissionForUser(selectedUser.getName(),
95 94
selectedPermission, new AsyncCallback<SuccessInfo>() {
96
-
97 95
@Override
98 96
public void onFailure(Throwable caught) {
99 97
Window.alert(caught.getMessage());
... ...
@@ -117,7 +115,6 @@ public class WildcardPermissionPanel extends HorizontalPanel
117 115
suggestPermission.addKeyUpHandler(event -> addPermissionButtonUpdater.execute());
118 116
suggestPermission.addSelectionHandler(event -> addPermissionButtonUpdater.execute());
119 117
permissionInputPanel.add(addPermissionButton);
120
-
121 118
// create permission table
122 119
wildcardPermissionWithSecurityDTOTableWrapper = new WildcardPermissionWithSecurityDTOTableWrapper(userService,
123 120
stringMessages, errorReporter, /* enablePager */ true, tableResources, this.userSelectionModel,
... ...
@@ -126,18 +123,14 @@ public class WildcardPermissionPanel extends HorizontalPanel
126 123
final LabeledAbstractFilterablePanel<WildcardPermissionWithSecurityDTO> userFilterbox = wildcardPermissionWithSecurityDTOTableWrapper
127 124
.getFilterField();
128 125
userFilterbox.getElement().setPropertyString("placeholder", stringMessages.filterUserGroups());
129
-
130 126
// add elements to permission panel + add caption
131 127
final VerticalPanel permissionPanel = new VerticalPanel();
132 128
permissionPanel.add(permissionInputPanel);
133 129
permissionPanel.add(userFilterbox);
134 130
permissionPanel.add(scrollPanel);
135
-
136 131
final CaptionPanel captionPanel = new CaptionPanel(stringMessages.permissions());
137 132
captionPanel.add(permissionPanel);
138
-
139 133
this.setVisible(false);
140
-
141 134
add(captionPanel);
142 135
}
143 136
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java
... ...
@@ -68,13 +68,10 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
68 68
}, tableResources);
69 69
this.userSelectionModel = userSelectionModel;
70 70
this.userSelectionModel.addSelectionChangeHandler(e -> refreshPermissionList());
71
-
72 71
final ListHandler<WildcardPermissionWithSecurityDTO> userColumnListHandler = getColumnSortHandler();
73
-
74 72
// users table
75 73
final TextColumn<WildcardPermissionWithSecurityDTO> userGroupWithSecurityDTONameColumn = new AbstractSortableTextColumn<WildcardPermissionWithSecurityDTO>(
76 74
dto -> dto.toString(), userColumnListHandler);
77
-
78 75
final AccessControlledActionsColumn<WildcardPermissionWithSecurityDTO, PermissionAndRoleImagesBarCell> userActionColumn = create(
79 76
new PermissionAndRoleImagesBarCell(stringMessages), userService);
80 77
userActionColumn.addAction(ACTION_DELETE, DELETE, selectedPermission -> {
... ...
@@ -82,7 +79,6 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
82 79
if (selectedObject != null) {
83 80
userService.getUserManagementService().removePermissionFromUser(selectedObject.getName(),
84 81
selectedPermission, new AsyncCallback<SuccessInfo>() {
85
-
86 82
@Override
87 83
public void onFailure(Throwable caught) {
88 84
Window.alert(stringMessages.couldNotRemovePermissionFromUser(selectedObject.getName(),
... ...
@@ -103,19 +99,15 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
103 99
Window.alert(stringMessages.pleaseSelect());
104 100
}
105 101
});
106
-
107 102
final HasPermissions type = SecuredSecurityTypes.PERMISSION_ASSOCIATION;
108
-
109 103
final EditOwnershipDialog.DialogConfig<WildcardPermissionWithSecurityDTO> configOwnership = EditOwnershipDialog
110 104
.create(userService.getUserManagementService(), type, permission -> refreshPermissionList(),
111 105
stringMessages);
112
-
113 106
final EditACLDialog.DialogConfig<WildcardPermissionWithSecurityDTO> configACL = EditACLDialog.create(
114 107
userService.getUserManagementService(), type, user -> user.getAccessControlList(), stringMessages);
115 108
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openDialog);
116 109
userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
117 110
permission -> configACL.openDialog(permission));
118
-
119 111
// filter field configuration
120 112
filterField = new LabeledAbstractFilterablePanel<WildcardPermissionWithSecurityDTO>(
121 113
new Label(stringMessages.filterPermission()), new ArrayList<WildcardPermissionWithSecurityDTO>(),
... ...
@@ -123,7 +115,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
123 115
@Override
124 116
public Iterable<String> getSearchableStrings(WildcardPermissionWithSecurityDTO t) {
125 117
List<String> string = new ArrayList<String>();
126
- string.add(t.getName());
118
+ string.add(t.toString());
127 119
return string;
128 120
}
129 121
... ...
@@ -135,9 +127,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
135 127
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
136 128
filterField
137 129
.setUpdatePermissionFilterForCheckbox(permission -> userService.hasPermission(permission, DefaultActions.UPDATE));
138
-
139 130
mainPanel.insert(filterField, 0);
140
-
141 131
// setup table
142 132
table.addColumnSortHandler(userColumnListHandler);
143 133
table.addColumn(userGroupWithSecurityDTONameColumn, stringMessages.permission());
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java
... ...
@@ -65,13 +65,10 @@ public class RoleWithSecurityDTOTableWrapper extends
65 65
}, tableResources);
66 66
this.userSelectionModel = userSelectionModel;
67 67
this.userSelectionModel.addSelectionChangeHandler(e -> refreshRoleList());
68
-
69 68
final ListHandler<RoleWithSecurityDTO> userColumnListHandler = getColumnSortHandler();
70
-
71 69
// users table
72 70
final TextColumn<RoleWithSecurityDTO> userGroupWithSecurityDTONameColumn = new AbstractSortableTextColumn<RoleWithSecurityDTO>(
73 71
dto -> dto.toString(), userColumnListHandler);
74
-
75 72
// add action column
76 73
final AccessControlledActionsColumn<RoleWithSecurityDTO, PermissionAndRoleImagesBarCell> userActionColumn = create(
77 74
new PermissionAndRoleImagesBarCell(stringMessages), userService);
... ...
@@ -85,7 +82,6 @@ public class RoleWithSecurityDTOTableWrapper extends
85 82
selectedRole.getRoleDefinition().getId(),
86 83
qualifiedForTenant == null ? null : qualifiedForTenant.getName(),
87 84
new AsyncCallback<SuccessInfo>() {
88
-
89 85
@Override
90 86
public void onFailure(Throwable caught) {
91 87
Window.alert(stringMessages.couldNotRemoveRoleFromUser(selectedObject.getName(),
... ...
@@ -106,25 +102,21 @@ public class RoleWithSecurityDTOTableWrapper extends
106 102
Window.alert(stringMessages.pleaseSelect());
107 103
}
108 104
});
109
-
110 105
final HasPermissions type = SecuredSecurityTypes.PERMISSION_ASSOCIATION;
111
-
112 106
final EditOwnershipDialog.DialogConfig<RoleWithSecurityDTO> configOwnership = EditOwnershipDialog
113 107
.create(userService.getUserManagementService(), type, permission -> refreshRoleList(), stringMessages);
114
-
115 108
final EditACLDialog.DialogConfig<RoleWithSecurityDTO> configACL = EditACLDialog.create(
116 109
userService.getUserManagementService(), type, user -> user.getAccessControlList(), stringMessages);
117 110
userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openDialog);
118 111
userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
119 112
permission -> configACL.openDialog(permission));
120
-
121 113
// filter field configuration
122 114
filterField = new LabeledAbstractFilterablePanel<RoleWithSecurityDTO>(new Label(stringMessages.filterRoles()),
123 115
new ArrayList<RoleWithSecurityDTO>(), dataProvider, stringMessages) {
124 116
@Override
125 117
public Iterable<String> getSearchableStrings(RoleWithSecurityDTO t) {
126 118
List<String> string = new ArrayList<String>();
127
- string.add(t.getName());
119
+ string.add(t.toString());
128 120
return string;
129 121
}
130 122
... ...
@@ -135,9 +127,7 @@ public class RoleWithSecurityDTOTableWrapper extends
135 127
};
136 128
filterField.setUpdatePermissionFilterForCheckbox(role -> userService.hasPermission(role, DefaultActions.UPDATE));
137 129
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
138
-
139 130
mainPanel.insert(filterField, 0);
140
-
141 131
// setup table
142 132
table.addColumnSortHandler(userColumnListHandler);
143 133
table.addColumn(userGroupWithSecurityDTONameColumn, stringMessages.roleName());
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/UserRoleDefinitionPanel.java
... ...
@@ -1,5 +1,7 @@
1 1
package com.sap.sse.security.ui.client.usermanagement.roles;
2 2
3
+import java.util.function.Function;
4
+import java.util.function.Supplier;
3 5
import java.util.stream.Collectors;
4 6
import java.util.stream.StreamSupport;
5 7
... ...
@@ -17,6 +19,7 @@ import com.google.gwt.user.client.ui.CaptionPanel;
17 19
import com.google.gwt.user.client.ui.HorizontalPanel;
18 20
import com.google.gwt.user.client.ui.ScrollPanel;
19 21
import com.google.gwt.user.client.ui.SuggestBox;
22
+import com.google.gwt.user.client.ui.SuggestOracle;
20 23
import com.google.gwt.user.client.ui.TextBox;
21 24
import com.google.gwt.user.client.ui.UIObject;
22 25
import com.google.gwt.user.client.ui.VerticalPanel;
... ...
@@ -53,8 +56,8 @@ public class UserRoleDefinitionPanel extends HorizontalPanel
53 56
54 57
public UserRoleDefinitionPanel(final UserService userService, final StringMessages stringMessages,
55 58
final ErrorReporter errorReporter, final CellTableWithCheckboxResources tableResources,
56
- final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers) {
57
-
59
+ final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers,
60
+ Function<SuggestOracle, SuggestBox> suggestBoxConstructor, Supplier<TextBox> textBoxConstructor) {
58 61
// create multi to single selection adapter
59 62
final SingleSelectionModel<UserDTO> multiToSingleSelectionModelAdapter = new SingleSelectionModel<>();
60 63
userSelectionModel.addSelectionChangeHandler(event -> {
... ...
@@ -71,29 +74,23 @@ public class UserRoleDefinitionPanel extends HorizontalPanel
71 74
}
72 75
});
73 76
this.userSelectionModel = multiToSingleSelectionModelAdapter;
74
-
75 77
// create role suggest
76 78
oracle = new RoleDefinitionSuggestOracle(
77 79
userService.getUserManagementService(), stringMessages);
78
- suggestRole = new SuggestBox(oracle);
80
+ suggestRole = suggestBoxConstructor.apply(oracle);
79 81
roleAndPermissionDetailsResources.css().ensureInjected();
80 82
suggestRole.addStyleName(roleAndPermissionDetailsResources.css().enterRoleNameSuggest());
81 83
suggestRole.getElement().setPropertyString("placeholder", stringMessages.enterRoleName());
82 84
this.initPlaceholder(suggestRole, stringMessages.enterRoleName());
83
-
84 85
// create role input panel + add controls
85 86
final HorizontalPanel roleInputPanel = new HorizontalPanel();
86
-
87
- final TextBox tenantInput = new TextBox();
87
+ final TextBox tenantInput = textBoxConstructor.get();
88 88
this.initPlaceholder(tenantInput, stringMessages.groupName());
89
-
90
- final TextBox userInput = new TextBox();
89
+ final TextBox userInput = textBoxConstructor.get();
91 90
this.initPlaceholder(userInput, stringMessages.username());
92
-
93 91
roleInputPanel.add(suggestRole);
94 92
roleInputPanel.add(tenantInput);
95 93
roleInputPanel.add(userInput);
96
-
97 94
final Button addRoleButton = new Button(stringMessages.add(), (ClickHandler) event -> {
98 95
StrippedRoleDefinitionDTO role = oracle.fromString(suggestRole.getText());
99 96
if (role != null) {
... ...
@@ -101,7 +98,6 @@ public class UserRoleDefinitionPanel extends HorizontalPanel
101 98
if (selectedUser != null) {
102 99
userService.getUserManagementService().addRoleToUser(selectedUser.getName(), userInput.getText(),
103 100
role.getId(), tenantInput.getText(), new AsyncCallback<SuccessInfo>() {
104
-
105 101
@Override
106 102
public void onFailure(Throwable caught) {
107 103
Window.alert(caught.getMessage());
... ...
@@ -126,7 +122,6 @@ public class UserRoleDefinitionPanel extends HorizontalPanel
126 122
suggestRole.addKeyUpHandler(event -> addRoleButtonUpdater.execute());
127 123
suggestRole.addSelectionHandler(event -> addRoleButtonUpdater.execute());
128 124
roleInputPanel.add(addRoleButton);
129
-
130 125
// create role table
131 126
roleWithSecurityDTOTableWrapper = new RoleWithSecurityDTOTableWrapper(userService, stringMessages,
132 127
errorReporter, /* enablePager */ true, tableResources, this.userSelectionModel, updateUsers);
... ...
@@ -134,18 +129,14 @@ public class UserRoleDefinitionPanel extends HorizontalPanel
134 129
final LabeledAbstractFilterablePanel<RoleWithSecurityDTO> userFilterbox = roleWithSecurityDTOTableWrapper
135 130
.getFilterField();
136 131
userFilterbox.getElement().setPropertyString("placeholder", stringMessages.filterUserGroups());
137
-
138 132
// add elements to role panel + add caption
139 133
final VerticalPanel rolePanel = new VerticalPanel();
140 134
rolePanel.add(roleInputPanel);
141 135
rolePanel.add(userFilterbox);
142 136
rolePanel.add(scrollPanel);
143
-
144 137
final CaptionPanel captionPanel = new CaptionPanel(stringMessages.roles());
145 138
captionPanel.add(rolePanel);
146
-
147 139
this.setVisible(false);
148
-
149 140
add(captionPanel);
150 141
}
151 142
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
... ...
@@ -504,7 +504,7 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
504 504
public User call() throws Exception {
505 505
if (userGroupExists(username + SecurityService.TENANT_SUFFIX)) {
506 506
throw new UserManagementException(
507
- "User tenant already exists, please chose a different username!");
507
+ "User "+username+" already exists, please chose a different username!");
508 508
}
509 509
try {
510 510
User newUser = getSecurityService().createSimpleUser(username, email, password, fullName,
... ...
@@ -880,7 +880,6 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U
880 880
.getQualifiedObjectIdentifier(associationTypeIdentifier);
881 881
getSecurityService().addToAccessControlList(qualifiedObjectAssociationIdentifier,
882 882
null, DefaultActions.READ.name());
883
-
884 883
getSecurityService().addRoleForUser(user, role);
885 884
logger.info(message);
886 885
}
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java
... ...
@@ -381,7 +381,6 @@ public class AccessControlStoreImpl implements AccessControlStore {
381 381
public Iterable<OwnershipAnnotation> getOwnerships() {
382 382
return LockUtil.executeWithReadLockAndResult(lockForManagementMappings,
383 383
new RunnableWithResult<Iterable<OwnershipAnnotation>>() {
384
-
385 384
@Override
386 385
public Iterable<OwnershipAnnotation> run() {
387 386
return new ArrayList<>(ownerships.values());
... ...
@@ -392,7 +391,6 @@ public class AccessControlStoreImpl implements AccessControlStore {
392 391
@Override
393 392
public void clear() {
394 393
LockUtil.executeWithWriteLock(lockForManagementMappings, new Runnable() {
395
-
396 394
@Override
397 395
public void run() {
398 396
accessControlLists.clear();
java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
... ...
@@ -146,7 +146,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
146 146
Iterable<UserGroup> getUserGroupsOfUser(User user);
147 147
148 148
UserGroup createUserGroup(UUID id, String name) throws UserGroupManagementException;
149
-
149
+
150 150
void addUserToUserGroup(UserGroup group, User user);
151 151
152 152
void removeUserFromUserGroup(UserGroup group, User user);
java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
153 153
index 1ad000e..4f0a66e
154
--- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
154
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
... ...
@@ -29,6 +29,7 @@ import java.util.function.Consumer;
29 29
import java.util.function.Function;
30 30
import java.util.logging.Level;
31 31
import java.util.logging.Logger;
32
+import java.util.stream.Collectors;
32 33
33 34
import javax.servlet.Filter;
34 35
import javax.servlet.ServletContext;
... ...
@@ -170,7 +171,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
170 171
171 172
private Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication;
172 173
173
- private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false);
174
+ private volatile boolean currentlyFillingFromInitialLoad;
174 175
175 176
private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false);
176 177
... ...
@@ -239,6 +240,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
239 240
AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider,
240 241
String sharedAcrossSubdomainsOf, String baseUrlForCrossDomainStorage) {
241 242
logger.info("Initializing Security Service with user store " + userStore);
243
+ this.currentlyFillingFromInitialLoad = false;
244
+ this.currentlyFillingFromInitialLoad = false;
242 245
operationsSentToMasterForReplication = new HashSet<>();
243 246
this.sharedAcrossSubdomainsOf = sharedAcrossSubdomainsOf;
244 247
this.baseUrlForCrossDomainStorage = baseUrlForCrossDomainStorage;
... ...
@@ -287,12 +290,12 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
287 290
288 291
@Override
289 292
public boolean isCurrentlyFillingFromInitialLoad() {
290
- return currentlyFillingFromInitialLoad.get();
293
+ return currentlyFillingFromInitialLoad;
291 294
}
292 295
293 296
@Override
294 297
public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) {
295
- this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad);
298
+ this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad;
296 299
}
297 300
298 301
@Override
... ...
@@ -540,10 +543,14 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
540 543
}
541 544
for (Map.Entry<UserGroup, Set<String>> entry : permissionMap.entrySet()) {
542 545
final UserGroup userGroup = entry.getKey();
546
+ // filter any denied action for anonymous user
547
+ Set<String> filteredActions = entry.getValue().stream()
548
+ .filter(action -> !(entry.getKey() == null && action != null && action.startsWith("!")))
549
+ .collect(Collectors.toSet());
550
+
543 551
final UUID userGroupId = userGroup == null ? null : userGroup.getId();
544
- final Set<String> actions = entry.getValue();
545 552
// avoid the UserGroup object having to be serialized with the operation by using the ID
546
- apply(s -> s.internalAclPutPermissions(idOfAccessControlledObject, userGroupId, actions));
553
+ apply(s -> s.internalAclPutPermissions(idOfAccessControlledObject, userGroupId, filteredActions));
547 554
}
548 555
return accessControlStore.getAccessControlList(idOfAccessControlledObject).getAnnotation();
549 556
}
java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/UserGroupResource.java
... ...
@@ -122,8 +122,8 @@ public class UserGroupResource extends AbstractSecurityResource {
122 122
final Response response;
123 123
final JSONObject json = (JSONObject) JSONValue.parse(jsonBody);
124 124
final String groupName = (String) json.get(KEY_GROUP_NAME);
125
- final UserGroup usergroup = getService().getUserGroupByName(groupName);
126
- if (usergroup != null) {
125
+ final UserGroup existingUserGroup = getService().getUserGroupByName(groupName);
126
+ if (existingUserGroup != null) {
127 127
response = Response.status(Status.BAD_REQUEST).entity("Usergroup with this name already exists.").build();
128 128
} else {
129 129
UUID newTenantId = UUID.randomUUID();
mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorGoalPassingComparator.java
... ...
@@ -10,13 +10,16 @@ import java.util.concurrent.ConcurrentMap;
10 10
import com.sap.sailing.domain.base.Boat;
11 11
import com.sap.sailing.domain.base.Competitor;
12 12
import com.sap.sse.common.Util;
13
+import com.sap.sse.common.util.NaturalComparator;
13 14
14 15
public class CompetitorGoalPassingComparator implements Comparator<Map.Entry<Competitor, Boat>> {
15 16
16 17
private ConcurrentMap<UUID, Integer> ranking;
18
+ private final NaturalComparator naturalComparator;
17 19
18 20
public CompetitorGoalPassingComparator() {
19 21
this.ranking = new ConcurrentHashMap<>();
22
+ this.naturalComparator = new NaturalComparator();
20 23
}
21 24
22 25
public ConcurrentMap<UUID, Integer> updateWith(List<Util.Pair<Long, String>> sortedList) {
... ...
@@ -34,6 +37,26 @@ public class CompetitorGoalPassingComparator implements Comparator<Map.Entry<Com
34 37
public int compare(Map.Entry<Competitor, Boat> leftCompetitor, Map.Entry<Competitor, Boat> rightCompetitor) {
35 38
Integer leftRank = this.ranking.get(leftCompetitor.getKey().getId());
36 39
Integer rightRank = this.ranking.get(rightCompetitor.getKey().getId());
40
+ if (leftRank == null && rightRank == null) {
41
+ //Compare further properties
42
+ //Sail ID
43
+ int result = CompetitorSailIdComparator.compare(leftCompetitor.getValue().getSailID(), rightCompetitor.getValue().getSailID(), naturalComparator);
44
+ if (result == 0) {
45
+ //Short name
46
+ result = CompetitorShortNameComparator.compare(leftCompetitor.getKey().getShortName(), rightCompetitor.getKey().getShortName(), naturalComparator);
47
+ }
48
+ if (result == 0) {
49
+ //Competitor name
50
+ result = naturalComparator.compare(leftCompetitor.getKey().getName(), rightCompetitor.getKey().getName());
51
+ }
52
+ return result;
53
+ }
54
+ if (leftRank == null) {
55
+ return 1;
56
+ }
57
+ if (rightRank == null) {
58
+ return -1;
59
+ }
37 60
return leftRank - rightRank;
38 61
}
39 62
}
wiki/info/landscape/amazon-ec2.md
... ...
@@ -12,82 +12,22 @@
12 12
13 13
#### Starting an instance
14 14
15
+To start with, your user account needs to have sufficient permissions to create a new server group ``{NEWSERVERNAME}-server`` up-front so that you have at least the permissions granted by the ``user`` role for all objects owned by that group. Change the group's group ownership so that the new group is its own group owner. Additionally, in order to have the new server participate in the shared security service and shared sailing data service on ``security-service.sapsailing.com`` your user needs ``SERVER:REPLICATE:security-service``. Your user should also have the ``SERVER:*:{NEWSERVERNAME}`` permission (e.g., implied by the more general ``SERVER:*`` permission), e.g., granted by the ``server_admin`` role. The latter permission is helpful in order to be able to configure the resulting server and to set up replication for it. If your user account currently does not have those permissions, find an administrator who has at least ``SERVER:*`` which is implied in particular by having role ``server_admin:*``. Such an administrator will be able to grant you the ``SERVER``-related permissions described here.
15 16
16
-- Which instance type to choose:
17
- - Archive: m2.2xlarge
18
- - Live: c1.xlarge
17
+Now start by creating the new server group, named ``{NEWSERVERNAME}-server``. So for example, if your server will use ``SERVER_NAME=abc`` then create a user group called ``abc-server``. You will yourself be a member of that new group automatically. Add role ``user`` to the group, enabling it only for the members of the group ("Enabled for all users" set to "No"). This way, all members of the group will gain permissions for objects owned by that server as if they owned them themselves. This also goes for the new ``SERVER`` object, but owners only obtain permissions for default actions, not the dedicated ``SERVER`` actions.
19 18
20
-You may need to select "All generations" instead of "Current generation" to see these instance configurations. Of course, you may choose variations of those as you feel is appropriate for your use case.
21
-
22
-- Using a release, set the following in the instance's user data, replacing `myspecificevent` by a unique name of the event or series you'll be running on that instance, such as `kielerwoche2014` or similar.
23
-
24
- ```
25
- INSTALL_FROM_RELEASE=`name-of-release`
26
- USE_ENVIRONMENT=live-server
27
- MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true"
28
- REPLICATION_CHANNEL=myspecificevent
29
- SERVER_NAME=MYSPECIFICEVENT
30
- BUILD_COMPLETE_NOTIFY=your@email.here
31
- SERVER_STARTUP_NOTIFY=your@email.here
32
- ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.domain.tracking.MailInvitationType=SailInsight2"
33
- # Provide authentication credentials for a user on security-service.sapsailing.com permitted to replicate, either by username/password...
34
- #REPLICATE_MASTER_USERNAME=(user for replicator login on security-service.sapsailing.com server having SERVER:REPLICATE:&lt;server-name&gt; permission)
35
- #REPLICATE_MASTER_PASSWORD=(password of the user for replication login on security-service.sapsailing.com)
36
- # Or by bearer token, obtained, e.g., through
37
- # curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token
38
- # or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to
39
- # https://security-service.sapsailing.com/security/api/restsecurity/access_token
40
- #REPLICATE_MASTER_BEARER_TOKEN=
41
-```
42
-
43
-The *MailInvitationType* property controls which version of the SAP Sail Insight app will be targeted by tracking invitations sent out by e-mail.
44
-Two different Branch.io URL schemes exist for the Sail Insight app: sailinsight-app.sapsailing.com and sailinsight20-app.sapsailing.com.
45
-They can be selected by providing *SailInsight1* or *SailInsight2*, respectively, as the values for the property. If the property is
46
-set to *LEGACY*, no Branch.io link is used in the invitation at all. This mode should no longer be used because the Branch.io-enabled
47
-iOS app has hit the store. If not provided, it will default to *SailInsight2*.
48
-
49
-Note that when you select to install an environment using the `USE_ENVIRONMENT` variable, any other variable that you specify in the user data, such as the `MONGODB_URI` or `REPLICATION_CHANNEL` properties in the example above, these additional user data properties will override whatever comes from the environment specified by the `USE_ENVIRONMENT` parameter.
50
-
51
-- To build from git, install and start, set the following in the instance's user data, adjusting the branch name (`BUILD_FROM`), the `myspecificevent` naming and memory settings according to your needs:
52
-
53
-```
54
- BUILD_BEFORE_START=True
55
- BUILD_FROM=master
56
- RUN_TESTS=False
57
- COMPILE_GWT=True
58
- BUILD_COMPLETE_NOTIFY=you@email.com
59
- SERVER_STARTUP_NOTIFY=
60
- SERVER_NAME=MYSPECIFICEVENT
61
- MEMORY=2048m
62
- REPLICATION_HOST=rabbit.internal.sapsailing.com
63
- REPLICATION_CHANNEL=myspecificevent
64
- MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true"
65
- # Provide authentication credentials for a user on security-service.sapsailing.com permitted to replicate, either by username/password...
66
- #REPLICATE_MASTER_USERNAME=(user for replicator login on security-service.sapsailing.com server having SERVER:REPLICATE:&lt;server-name&gt; permission)
67
- #REPLICATE_MASTER_PASSWORD=(password of the user for replication login on security-service.sapsailing.com)
68
- # Or by bearer token, obtained, e.g., through
69
- # curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token
70
- # or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to
71
- # https://security-service.sapsailing.com/security/api/restsecurity/access_token
72
- #REPLICATE_MASTER_BEARER_TOKEN=
73
-```
74
-
75
-#### Setting up a new image (AMI) from scratch (more or less)
76
-
77
-See [here](/wiki/creating-ec2-image-from-scratch)
78
-
79
-#### Receiving wind from Expedition
19
+Now choose the instance type to start. For example:
20
+ - Archive server: i3.2xlarge
21
+ - Live event: c4.2xlarge
80 22
81
-- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/servers/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server.
82
-
83
-#### Setting up Master and Replica
23
+You may need to select "All generations" instead of "Current generation" to see these instance configurations. Of course, you may choose variations of those as you feel is appropriate for your use case.
84 24
85
-- Fire up a master with the following configuration. There is a preconfigured master environment at http://releases.sapsailing.com/environments/live-master-server that you should use.
25
+Using a release, set the following in the instance's user data, replacing `myspecificevent` by a unique name of the event or series you'll be running on that instance, such as `kielerwoche2014` or similar. Note that when you select to install an environment using the `USE_ENVIRONMENT` variable, any other variable that you specify in the user data, such as the `MONGODB_URI` or `REPLICATION_CHANNEL` properties in the example above, these additional user data properties will override whatever comes from the environment specified by the `USE_ENVIRONMENT` parameter.
86 26
87 27
```
88 28
INSTALL_FROM_RELEASE=(name-of-release)
89 29
USE_ENVIRONMENT=live-master-server
90
-SERVER_NAME=MYSPECIFICEVENT
30
+SERVER_NAME=myspecificevent
91 31
REPLICATION_CHANNEL=myspecificevent
92 32
MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true"
93 33
SERVER_STARTUP_NOTIFY=you@email.com
... ...
@@ -98,17 +38,22 @@ SERVER_STARTUP_NOTIFY=you@email.com
98 38
# curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token
99 39
# or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to
100 40
# https://security-service.sapsailing.com/security/api/restsecurity/access_token
101
-#REPLICATE_MASTER_BEARER_TOKEN=
41
+REPLICATE_MASTER_BEARER_TOKEN=(a bearer token allowing this master to replicate from security-service.sapsailing.com)
102 42
```
103 43
104
-- After your master server is ready, note the internal IP and configure your replica instances. Set up a user account there that has the following permission: ``SERVER:REPLICATE:{SERVERNAME}``. You will need this user's credentials to authenticate your replicas for replication.
44
+Have at least a public-facing target group ready. If you want to expose the master to the public (single-instance scenario or master-replica scenario where the master also handles reading client requests) add the master to the public target group.
105 45
106
-- Make sure to use the preconfigured environment from http://releases.sapsailing.com/environments/live-replica-server. Then absolutely make sure to add the line "REPLICATE_MASTER_SERVLET_HOST" to the user-data and adjust the `myspecificevent` master exchange name to the `REPLICATION_CHANNEL` setting you used for the master configuration.
46
+If you want to launch one or more replicas, ensure you have a dedicated ``...-master`` target group to which you add your master instance, and a load balancer rule that forwards your replica's requests directed to the master to that ``...-master`` target group, for example, by using a dedicated ``...-master`` hostname rule in your load balancer which then forwards to the ``...-master`` target group.
47
+
48
+After your master server is ready, note the internal IP and configure your replica instances if you'd like to connect using the master's IP address. Alternatively, you may route the replica requests through the load balancer again, using whatever your load balancer requires to route the requests to your master, such as the ``...-master`` hostname with HTTPS as a protocol and 443 for a port. If you don't want to use the credentials of your own user account (which is expected to have permission ``SERVER:REPLICATE:{SERVERNAME}`` already because as described above you need this for configuring the new server), e.g., because you then have to expose an access token in the environment that anyone with SSH access to the instance may be able to see, set up a new user account, such as ``{SERVERNAME}-replicator``, that has the following permission: ``SERVER:REPLICATE:{SERVERNAME}`` where ``{SERVERNAME}`` is what you provided above for the ``SERVER_NAME`` environment variable. You will be able to grant this permission to the new user because your own user account is expected to have this permission. You will need your own or this new user's credentials to authenticate your replicas for replication.
49
+
50
+Make sure to use the preconfigured environment from http://releases.sapsailing.com/environments/live-replica-server. Then absolutely make sure to add the line "REPLICATE_MASTER_SERVLET_HOST" to the user-data and adjust the `myspecificevent` master exchange name in the replica's ``REPLICATE_MASTER_EXCHANGE_NAME`` variable to the value of the ``REPLICATION_CHANNEL`` setting you used for the master configuration. Also ensure that you provide the ``REPLICATE_MASTER_BEARER_TOKEN`` value (or, alternatively ``REPLICATE_MASTER_USERNAME`` and ``REPLICATE_MASTER_PASSWORD``) to grant the replica the permissions it needs to successfully register with the master as a replica.
107 51
108 52
```
109 53
INSTALL_FROM_RELEASE=(name-of-release)
110 54
USE_ENVIRONMENT=live-replica-server
111
-REPLICATE_MASTER_SERVLET_HOST=(IP of your master server)
55
+REPLICATE_MASTER_SERVLET_HOST=(IP of your master server or external -master hostname)
56
+REPLICATE_MASTER_SERVLET_PORT=(port your master is listening on for HTTP/HTTPS requests; defaults to 8888; use 443 for -master hostname)
112 57
REPLICATE_MASTER_EXCHANGE_NAME=myspecificevent
113 58
# Provide authentication credentials for a user on the master permitted to replicate, either by username/password...
114 59
#REPLICATE_MASTER_USERNAME=(user for replicator login on master server having SERVER:REPLICATE:&lt;server-name&gt; permission)
... ...
@@ -117,13 +62,21 @@ REPLICATE_MASTER_EXCHANGE_NAME=myspecificevent
117 62
# curl -d "username=myuser&password=mysecretpassword" "https://master-server.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token
118 63
# or by logging in to the master server using your web browser and then navigating to
119 64
# https://master-server.sapsailing.com/security/api/restsecurity/access_token
120
-# REPLICATE_MASTER_BEARER_TOKEN=
65
+REPLICATE_MASTER_BEARER_TOKEN=(a bearer token allowing this master to replicate from your master)
121 66
SERVER_NAME=MYSPECIFICEVENT
122 67
MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent-replica?replicaSet=live&retryWrites=true"
123 68
EVENT_ID=&lt;some-uuid-of-an-event-you-want-to-feature&gt;
124 69
SERVER_STARTUP_NOTIFY=you@email.com
125 70
```
126 71
72
+#### Setting up a new image (AMI) from scratch (more or less)
73
+
74
+See [here](/wiki/creating-ec2-image-from-scratch)
75
+
76
+#### Receiving wind from Expedition
77
+
78
+- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/servers/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server.
79
+
127 80
#### Setting up a Multi Instance
128 81
To set up a multi instance for a server with name "SSV", subdomain "ssv.sapsailing.com" and description "Schwartauer Segler-Verein, [www.ssv-net.de](http://www.ssv-net.de), Alexander Probst, [webmaster@alexprobst.de](mailto:webmaster@alexprobst.de)" perform the following steps:
129 82
wiki/info/security/permission-concept.md
... ...
@@ -100,14 +100,16 @@ Additionally, on fresh instances not sharing a UserStore, a set of default roles
100 100
101 101
### Role "admin"
102 102
103
-This role implies the "*" permission. It should ideally be used with a user group ownership qualification, such as the server-specific group, making a user with a role qualified in this way an administrator for the server object as well as for all other objects owned by that server group.
103
+This role implies the "&ast;" permission. It should ideally be used with a user group ownership qualification, such as the server-specific group, making a user with a role qualified in this way an administrator for the server object as well as for all other objects owned by that server group. The ``admin`` role could also be used together with a user qualification, as in ``admin::{username}`` to grant a user full permissions to all objects owned by that user. Note that this in the future, as we introduce payed premium content, would include such premium permissions. Likewise, the ``admin`` role qualified to the user's default group ``{username}-tenant`` could be granted to users so they obtain full rights for all objects owned by their default ``{username}-tenant`` group.
104 104
105 105
### Role "user"
106 106
107 107
This role is intended to be used to describe the permissions that object owners shall be granted. In particular, when qualified
108 108
for objects owned by the user being assigned the role, that user obtains the permission to execute actions
109 109
``CHANGE_ACL,CHANGE_OWNERSHIP,CREATE,DELETE,READ,READ_PUBLIC,UPDATE`` for objects of any type. By default, users are also assigned
110
-this role constrained for objects whose group owner is the user's dedicated group (``{username}-tenant``).
110
+this role qualified for objects whose group owner is the user's dedicated group (``{username}-tenant``).
111
+
112
+Note that for good reasons this role does not imply all ("&ast;") permissions to object owners. All permissions would include features that we may want to charge a price for. The ``user`` role shall include only those permissions that all users with no premium permissions have. In a sense, the user role's permission set is the difference of the admin role's permission set ("&ast;") minus all premium permissions.
111 113
112 114
### Role "sailing_viewer"
113 115
wiki/info/security/security.md
... ...
@@ -1,6 +1,6 @@
1 1
# Security
2 2
3
-The Sports Sponsorships Engine (SSE) on which the SAP Sailing Analytics and the SAP Tennis Analytics are based, uses Apache Shiro to implement security. This in particular includes authentication and authorization. This document does not aim to replace the Shiro documentation which is available, e.g., at [http://shiro.apache.org/configuration.html](http://shiro.apache.org/configuration.html), [http://shiro.apache.org/permissions.html](http://shiro.apache.org/permissions.html) and [http://shiro.apache.org/web.html](http://shiro.apache.org/web.html).
3
+The Sports Sponsorships Engine (SSE) on which the SAP Sailing Analytics and the SAP Tennis Analytics are based, uses Apache Shiro to implement security. This in particular includes authentication and authorization. This document does not aim to replace the Shiro documentation which is available, e.g., at [http://shiro.apache.org/configuration.html](http://shiro.apache.org/configuration.html), [http://shiro.apache.org/permissions.html](http://shiro.apache.org/permissions.html) and [http://shiro.apache.org/web.html](http://shiro.apache.org/web.html). It relies on a sophisticated and powerful security engine that is part of SSE and whose [permission concept is described here](permission-concept).
4 4
5 5
## Users, Sessions, Roles, and Permissions
6 6
... ...
@@ -58,11 +58,23 @@ There are generally two ways in which some feature can require the user to be eq
58 58
59 59
Example for a declarative permission check:
60 60
[urls]
61
- /api/v1/events = bearerToken, perms["event:view"]
61
+ /api/v1/events = bearerToken, perms["EVENT:READ"]
62 62
This requires users trying to access the URL `/api/v1/events` to be authenticated using a valid `JSESSIONID` cookie or any authentication supported by the `bearerToken` filter such that the authenticated user has permissions that imply the `event:view:*` permission.
63 63
64
-Example for a programmatic check:
65
- SecurityUtils.getSubject().checkPermission("event:view");
64
+Examples for programmatic checks if the domain object, here ``event`` is at hand:
65
+
66
+```
67
+ SecurityUtils.getSubject().isPermitted(SecuredDomainType.EVENT.getStringPermissionForObject(DefaultActions.READ, event))
68
+```
69
+
70
+or
71
+
72
+```
73
+ SecurityUtils.getSubject().isPermitted(SecuredDomainType.EVENT.getStringPermissionForTypeRelativeIdentifier(
74
+ DefaultActions.UPDATE, EventBaseImpl.getTypeRelativeObjectIdentifier(eventId)))
75
+```
76
+
77
+if only an ID such as ``eventId`` is available at the point of the permission check.
66 78
67 79
### Special Case: Permission Checks in the AdminConsole
68 80