mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-13 22:15:26 +00:00
Merge branch 'master' into bug3677
This commit is contained in:
commit
ffda8c82ec
14 files changed
+187
-29
No files matched your search
+2
-2
@@ -10,7 +10,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
|
||||
|
||||
/**
|
||||
* {@link SensorFixMapper} implementation to handle {@link BravoFix}es.
|
||||
@@ -20,7 +20,7 @@ public class BravoDataFixMapper implements SensorFixMapper<BravoFix, DynamicSens
|
||||
@Override
|
||||
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
|
||||
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
|
||||
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
|
||||
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
|
||||
|
||||
/**
|
||||
* {@link SensorFixMapper} implementation to handle {@link BravoExtendedFix}es.
|
||||
@@ -21,7 +21,7 @@ public class BravoExtendedDataFixMapper implements SensorFixMapper<BravoFix, Dyn
|
||||
@Override
|
||||
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
|
||||
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
|
||||
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
|
||||
|
||||
/**
|
||||
* {@link SensorFixMapper} implementation to product {@link BravoExtendedFix}es from {@link DoubleVectorFix}es
|
||||
@@ -25,7 +25,7 @@ public class ExpeditionExtendedDataFixMapper implements SensorFixMapper<BravoFix
|
||||
@Override
|
||||
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
|
||||
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
|
||||
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -13,5 +13,4 @@ import com.sap.sse.common.WithID;
|
||||
*/
|
||||
public interface DynamicSensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix> extends
|
||||
SensorFixTrack<ItemType, FixT>, DynamicTrack<FixT> {
|
||||
|
||||
}
|
||||
@@ -25,4 +25,10 @@ public interface DynamicTrack<FixType extends Timed> extends Track<FixType> {
|
||||
* @return <code>true</code> if the element was added, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean add(FixType fix, boolean replace);
|
||||
|
||||
/**
|
||||
* Invoked when this track is added to a tracked race. The method is also invoked after de-serializing the
|
||||
* containing {@link TrackedRace}. This default implementation does nothing.
|
||||
*/
|
||||
default void addedToTrackedRace(TrackedRace trackedRace) {}
|
||||
}
|
||||
+21
-7
@@ -45,7 +45,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
* If a GPS track was provided at construction time, remember it non-transiently. It is needed when restoring
|
||||
* the object after de-serialization, so the cache invalidation listener can be re-established.
|
||||
*/
|
||||
private GPSFixTrack<ItemType, GPSFixMoving> gpsTrack;
|
||||
private transient GPSFixTrack<ItemType, GPSFixMoving> gpsTrack;
|
||||
|
||||
private class CacheInvalidationGpsTrackListener implements GPSTrackListener<ItemType, GPSFixMoving> {
|
||||
private static final long serialVersionUID = 6395529765232404414L;
|
||||
@@ -89,6 +89,23 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
this.foilingTimeCache = createFoilingTimeCache(trackedItem);
|
||||
this.foilingDistanceCache = createFoilingDistanceCache(trackedItem);
|
||||
this.averageRideHeightCache = createAverageRideHeightCache(trackedItem);
|
||||
setGpsTrack(gpsTrack);
|
||||
}
|
||||
|
||||
public GPSFixTrack<ItemType, GPSFixMoving> getGpsTrack() {
|
||||
return gpsTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #gpsTrack} field and registers a cache invalidation listener
|
||||
* on the {@code gpsTrack} to ensure that the foiling distance is invalidated if needed.<p>
|
||||
*
|
||||
* Must be called only once for a non-{@code null} {@code gpsTrack}.
|
||||
*/
|
||||
protected void setGpsTrack(GPSFixTrack<ItemType, GPSFixMoving> gpsTrack) {
|
||||
if (gpsTrack != null) {
|
||||
assert this.gpsTrack == null;
|
||||
}
|
||||
this.gpsTrack = gpsTrack;
|
||||
if (gpsTrack != null) {
|
||||
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
|
||||
@@ -119,9 +136,6 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
this.foilingTimeCache = createFoilingTimeCache(getTrackedItem());
|
||||
this.foilingDistanceCache = createFoilingDistanceCache(getTrackedItem());
|
||||
this.averageRideHeightCache = createAverageRideHeightCache(getTrackedItem());
|
||||
if (gpsTrack != null) {
|
||||
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,8 +229,8 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
|
||||
@Override
|
||||
public Distance getDistanceSpentFoiling(TimePoint from, TimePoint to) {
|
||||
assert gpsTrack != null;
|
||||
return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
|
||||
return getGpsTrack() == null ? null :
|
||||
getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
|
||||
/* valueCalculator */ new Track.TimeRangeValueCalculator<Distance>() {
|
||||
@Override
|
||||
public Distance calculate(TimePoint from, TimePoint to) {
|
||||
@@ -226,7 +240,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final boolean fixFoils = isFoiling(fix);
|
||||
if (isFoiling && fixFoils) {
|
||||
result = result.add(gpsTrack.getDistanceTraveled(last, fix.getTimePoint()));
|
||||
result = result.add(getGpsTrack().getDistanceTraveled(last, fix.getTimePoint()));
|
||||
}
|
||||
last = fix.getTimePoint();
|
||||
isFoiling = fixFoils;
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
public class CompetitorBravoFixTrackImpl extends BravoFixTrackImpl<Competitor> {
|
||||
private static final long serialVersionUID = 569731925060211875L;
|
||||
|
||||
public CompetitorBravoFixTrackImpl(Competitor trackedItem, String trackName, boolean hasExtendedFixes,
|
||||
GPSFixTrack<Competitor, GPSFixMoving> gpsTrack) {
|
||||
super(trackedItem, trackName, hasExtendedFixes, gpsTrack);
|
||||
}
|
||||
|
||||
public CompetitorBravoFixTrackImpl(Competitor trackedItem, String trackName, boolean hasExtendedFixes) {
|
||||
super(trackedItem, trackName, hasExtendedFixes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addedToTrackedRace(TrackedRace trackedRace) {
|
||||
if (this.getGpsTrack() == null) {
|
||||
this.setGpsTrack(trackedRace.getTrack(getTrackedItem()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -13,6 +13,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
@@ -609,7 +610,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
notifyListeners(listener -> listener.markPassingReceived(competitor, oldMarkPassings, markPassings));
|
||||
}
|
||||
|
||||
private void notifyListeners(DynamicSensorFixTrack<Competitor, ?> track) {
|
||||
private void notifyListenersAboutSensorTrackAdded(DynamicSensorFixTrack<Competitor, ?> track) {
|
||||
notifyListeners(listener -> listener.competitorSensorTrackAdded(track));
|
||||
}
|
||||
|
||||
@@ -1206,7 +1207,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <FixT extends SensorFix> void addSensorTrackInternal(Pair<Competitor, String> key,
|
||||
protected <FixT extends SensorFix> Optional<Runnable> addSensorTrackInternal(Pair<Competitor, String> key,
|
||||
DynamicSensorFixTrack<Competitor, FixT> track) {
|
||||
super.addSensorTrackInternal(key, track);
|
||||
track.addListener(new SensorFixTrackListener<Competitor, FixT>() {
|
||||
@@ -1222,6 +1223,6 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
notifyListeners(item, trackName, fix);
|
||||
}
|
||||
});
|
||||
notifyListeners(track);
|
||||
return Optional.of(()->notifyListenersAboutSensorTrackAdded(track));
|
||||
}
|
||||
}
|
||||
+24
-6
@@ -19,6 +19,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -635,6 +636,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException, PatchFailedException {
|
||||
ois.defaultReadObject();
|
||||
getRace().getCourse().addCourseListener(this);
|
||||
for (DynamicTrack<?> sensorTrack : sensorTracks.values()) {
|
||||
sensorTrack.addedToTrackedRace(this);
|
||||
}
|
||||
raceStates = new WeakHashMap<>();
|
||||
attachedRaceLogs = new ConcurrentHashMap<>();
|
||||
attachedRegattaLogs = new ConcurrentHashMap<>();
|
||||
@@ -4147,40 +4151,54 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
protected <FixT extends SensorFix, TrackT extends DynamicSensorFixTrack<Competitor, FixT>> TrackT getOrCreateSensorTrack(
|
||||
Competitor competitor, String trackName, TrackFactory<TrackT> newTrackFactory) {
|
||||
Pair<Competitor, String> key = new Pair<>(competitor, trackName);
|
||||
Optional<Runnable> executeAfterReleasingLock = Optional.empty();
|
||||
TrackT result;
|
||||
LockUtil.lockForWrite(sensorTracksLock);
|
||||
try {
|
||||
TrackT result = getTrackInternal(key);
|
||||
result = getTrackInternal(key);
|
||||
if (result == null && tracks.containsKey(competitor)) {
|
||||
// A track is only added if the given Competitor is known to participate in this race
|
||||
result = newTrackFactory.get();
|
||||
addSensorTrackInternal(key, result);
|
||||
executeAfterReleasingLock = addSensorTrackInternal(key, result);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(sensorTracksLock);
|
||||
}
|
||||
executeAfterReleasingLock.ifPresent(r->r.run());
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void addSensorTrack(Competitor competitor, String trackName, DynamicSensorFixTrack<Competitor, ?> track) {
|
||||
Pair<Competitor, String> key = new Pair<>(competitor, trackName);
|
||||
Optional<Runnable> executeAfterReleasingLock = Optional.empty();
|
||||
LockUtil.lockForWrite(sensorTracksLock);
|
||||
try {
|
||||
if(getTrackInternal(key) != null) {
|
||||
if (getTrackInternal(key) != null) {
|
||||
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.WARNING)) {
|
||||
logger.warning(SensorFixTrack.class.getName() + " already exists for competitor: "
|
||||
+ competitor.getName() + "; trackName: " + trackName);
|
||||
}
|
||||
} else {
|
||||
this.addSensorTrackInternal(key, track);
|
||||
executeAfterReleasingLock = this.addSensorTrackInternal(key, track);
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(sensorTracksLock);
|
||||
}
|
||||
executeAfterReleasingLock.ifPresent(r->r.run());
|
||||
}
|
||||
|
||||
protected <FixT extends SensorFix> void addSensorTrackInternal(Pair<Competitor, String> key,
|
||||
/**
|
||||
* To call this method, the caller must have obtained the write lock of {@link #sensorTracksLock}.
|
||||
* Optionally, the method may return a {@link Runnable} to execute after the lock has been released.
|
||||
* This may, e.g., be a routine that notifies listeners. Callers are responsible for invoking this
|
||||
* {@link Runnable} <em>after</em> releasing the write lock.
|
||||
*/
|
||||
protected <FixT extends SensorFix> Optional<Runnable> addSensorTrackInternal(Pair<Competitor, String> key,
|
||||
DynamicSensorFixTrack<Competitor, FixT> track) {
|
||||
assert sensorTracksLock.isWriteLockedByCurrentThread();
|
||||
sensorTracks.put(key, track);
|
||||
track.addedToTrackedRace(this);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
+2
-1
@@ -4815,7 +4815,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
URL base = createBaseUrl(urlAsString);
|
||||
String path = "/sailingserver/spi/v1/masterdata/leaderboardgroups";
|
||||
serverAddress = createUrl(base, path, query);
|
||||
connection = HttpUrlConnectionHelper.redirectConnection(serverAddress);
|
||||
// the response can take a very long time for MDI that include foiling data or such
|
||||
connection = HttpUrlConnectionHelper.redirectConnection(serverAddress, Duration.ONE_HOUR.times(2));
|
||||
getService().createOrUpdateDataImportProgressWithReplication(importOperationId, 0.02,
|
||||
DataImportSubProgress.CONNECTION_ESTABLISH, 0.5);
|
||||
if (compress) {
|
||||
|
||||
+22
@@ -3,6 +3,7 @@ package com.sap.sailing.server.replication.test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
@@ -45,11 +46,13 @@ import com.sap.sailing.domain.persistence.MongoWindStore;
|
||||
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
|
||||
import com.sap.sailing.domain.persistence.PersistenceFactory;
|
||||
import com.sap.sailing.domain.test.PositionAssert;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
|
||||
import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta;
|
||||
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
|
||||
import com.sap.sailing.server.operationaltransformation.CreateTrackedRace;
|
||||
@@ -138,6 +141,25 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See also bug4387: test that after replicating the TrackedRace the CompetitorBravoFixTrackImpl's gpsTrack object
|
||||
* points to the track from the TrackedRace to which the CompetitorBravoFixTrackImpl belongs.
|
||||
*/
|
||||
@Test
|
||||
public void testBravoFixTrackReplication() throws InterruptedException {
|
||||
trackedRace.getOrCreateSensorTrack(competitor, BravoFixTrack.TRACK_NAME, () -> new CompetitorBravoFixTrackImpl(competitor, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
|
||||
trackedRace.getTrack(competitor)));
|
||||
Thread.sleep(1000);
|
||||
DynamicTrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier);
|
||||
Competitor replicaCompetitor = replicaTrackedRace.getRace().getCompetitors().iterator().next();
|
||||
assertNotNull(replicaCompetitor);
|
||||
GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = replicaTrackedRace.getTrack(replicaCompetitor);
|
||||
CompetitorBravoFixTrackImpl competitorSensorTrack = replicaTrackedRace.getOrCreateSensorTrack(
|
||||
replicaCompetitor, BravoFixTrack.TRACK_NAME, () -> new CompetitorBravoFixTrackImpl(replicaCompetitor, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
|
||||
replicaTrackedRace.getTrack(replicaCompetitor)));
|
||||
assertSame(competitorTrack, competitorSensorTrack.getGpsTrack());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMarkFixReplication() throws InterruptedException {
|
||||
final GPSFixMovingImpl fix = new GPSFixMovingImpl(new DegreePosition(2, 3), new MillisecondsTimePoint(3456),
|
||||
|
||||
@@ -10,7 +10,11 @@ import com.sap.sse.common.Duration;
|
||||
public class HttpUrlConnectionHelper {
|
||||
private static final int HTTP_MAX_REDIRECTS = 5;
|
||||
|
||||
public static HttpURLConnection redirectConnection(URL url) throws MalformedURLException, IOException {
|
||||
/**
|
||||
* Redirects the connection using the <code>Location</code> header. Make sure to set
|
||||
* the timeout if you expect the response to take longer.
|
||||
*/
|
||||
public static HttpURLConnection redirectConnection(URL url, Duration timeout) throws MalformedURLException, IOException {
|
||||
HttpURLConnection connection = null;
|
||||
URL nextUrl = url;
|
||||
for (int counterOfRedirects = 0; counterOfRedirects <= HTTP_MAX_REDIRECTS; counterOfRedirects++) {
|
||||
@@ -21,8 +25,7 @@ public class HttpUrlConnectionHelper {
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0...");
|
||||
connection.setDoOutput(true);
|
||||
// Initial timeout needs to be big enough to allow the first parts of the response to reach this server
|
||||
connection.setReadTimeout((int) Duration.ONE_MINUTE.times(10).asMillis());
|
||||
connection.setReadTimeout((int) timeout.asMillis());
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM
|
||||
|| connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
|
||||
String location = connection.getHeaderField("Location");
|
||||
@@ -33,4 +36,8 @@ public class HttpUrlConnectionHelper {
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
public static HttpURLConnection redirectConnection(URL url) throws MalformedURLException, IOException {
|
||||
return redirectConnection(url, Duration.ONE_MINUTE.times(10));
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,8 @@ Bug 2822 changes the way competitors and boats are handled. Specifically the 1:1
|
||||
|
||||
Check:
|
||||
|
||||
- Existing COMPETITORS Collection is migrated to COMPETITORS_WITH_BOAT_REFERENCES and BOATS collection
|
||||
- Existing COMPETITORS Collection is renamed to COMPETITORS_BAK collection
|
||||
- Competitors from COMPETITORS_BAK collection will be migrated to COMPETITORS and BOATS collection
|
||||
- All migrated competitors have a link to a boat from boats collection
|
||||
- The attribute 'name' is cleared in new all created boat objects as the old names were not meaningful and not used
|
||||
|
||||
@@ -21,7 +22,7 @@ In case of a server instance with already created competitors we don't know to w
|
||||
Check:
|
||||
|
||||
- Before server start an existing competitor with emedded boat does exist in the COMPETITORS collection
|
||||
- After server start and migration according to 1.1 this competitor does exist in the COMPETITORS_WITH_BOAT_REFERENCES collection and does have a link to a boat in the BOATS collection
|
||||
- After server start and migration according to 1.1 this competitor does exist in the COMPETITORS collection and does have a link to a boat in the BOATS collection
|
||||
- After starting the tractrac tracking of a race where this competitor is contained the tractrac connector recognizes the need for the migration and calls the corresponding function on the CompetitorStore
|
||||
- After the migration the competitor does not have a boat reference anymore and the migrated boat has the right properties
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
* [Use Cases](#use cases)
|
||||
* [Algorithm for Composite Realm](#algorithm-bool-haspermission-wildcardpermission-permission-for-composite-realm)
|
||||
* [Migration](#migration)
|
||||
* [Implementation Details](#implementation-details)
|
||||
* [TODOs](#todos)
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -201,3 +203,64 @@ With such an extensive existing system as the Sailing Analytics Suite, migration
|
||||
Another challenge besides the code changes is the data migration. For every existing data object an ACL has to be created and filled with the right permissions so the users do not notice a big change.
|
||||
|
||||
Besides creating an ACL for every data objects that is access controlled, an owner has to be defined for each existing data object, so that in combination with the ACLs no user loses permissions they need to have. In order to do this, where will have to be a script that associates all data objects on a server with a tenant and a specific user as owner (e.g. on the archive all data objects are associated with the tenant “archive” and owned by the user “Axel”). Thereafter, the users on the server are assigned their respective role.
|
||||
|
||||
**Role Migration**
|
||||
|
||||
Following roles have to be migrated to dynamic roles. Beforehand they were hard coded in the SailingPermissionsForRoleProvider class. However, aside from the admin role, all roles listed here are only containing permissions that are checked in the frontend for showing/hiding tabs in the different menus.
|
||||
|
||||
* admin
|
||||
* "*"
|
||||
* eventmanager
|
||||
* "manage_media"
|
||||
* "manage_mark_passings"
|
||||
* "manage_mark_positions"
|
||||
* "manage_all_competitors"
|
||||
* "manage_course_layout"
|
||||
* "manage_device_configuration"
|
||||
* "manage_events"
|
||||
* "manage_igtimi_accounts"
|
||||
* "manage_leaderboard_groups"
|
||||
* "manage_leaderboards"
|
||||
* "manage_leaderboard_results"
|
||||
* "manage_racelog_tracking"
|
||||
* "manage_regattas"
|
||||
* "manage_result_import_urls"
|
||||
* "manage_structure_import_urls"
|
||||
* "manage_tracked_races"
|
||||
* "manage_wind"
|
||||
* "event"
|
||||
* "regatta"
|
||||
* "leaderboard"
|
||||
* "leaderboard_group"
|
||||
* mediaeditor
|
||||
* "manage_media"
|
||||
* moderator
|
||||
* "can_replay_during_live_races"
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Access control relevant objects are stored in the AccessControlStore, while user related objects are stored in the user, i.e. the UserStore. Tenants are currently stored as UserGroups in the user store. The algorithm outlined above is implemented in the PermissionChecker, which is one of the most central classes in the permission system. Therefrom it should be possible to find all other relevant classes. Other relevant classes include the AbstractCompositeAuthorizingRealm that implements permission checking in Shiro. Therefor it uses the PermissionChecker and is connected to the UserStore and the AccessControlStore. Furthermore the RolePermissionModel implements how roles imply permissions. The RolePermissionModel as well as the parameterization of roles need more work and are just a rough sketch. The UserManagementService exposes most of the access control relevant parts to the frontend. It works through the SecurityService with the AccessControlStore and the UserStore. Permission checking in the frontend is also done with the PermissionChecker. To ease the creation of WildcardPermissions, a PermissionBuilder was introduced that also needs some more work. All access control relevant UI parts can be found in the "Advanced" tab under "User management" and "Tenant management". A further tab "Access Control Management" should be introduced. Unit tests can be found in the package com.sap.sse.security.test.
|
||||
|
||||
## TODOs
|
||||
|
||||
Dump of TODOs. More structured in Bugzilla.
|
||||
|
||||
* Ownership/Access control list as expandable column in all access controlled data object tables
|
||||
* Permission checking for every domain data object type
|
||||
* Changing ownership (incl. replication)
|
||||
* Masterdataimport
|
||||
* Tenant management UI
|
||||
* Tenant in create dialog check if tenant exists (red outline of text input field)
|
||||
* Refresh tenant selection in tenant list on the left correctly
|
||||
* String messages
|
||||
* User management UI
|
||||
* Auto complete for roles and permissions
|
||||
* Access control list / Roles / Ownership UI
|
||||
* User role for editing own user parametrized by user name
|
||||
* Do we want to throw unauthorized exceptions or how should this work? (See addUserToTenant or removeUserFromTenant) / UnauthorizedException handling (i.e. popup that shows some text)
|
||||
* Create ownerships from config / Server-wide default owner and default tenant owner
|
||||
* Create test setup for final test
|
||||
* Button to transfer ownership of all objects from one user/tenant to another user/tenant (chgroup recursive?)
|
||||
* Rework meta permissions (rework to also check all permissions that are granted or revoked?)
|
||||
* Permission reloading without site refresh / Frontend update elements that require permission when permissions change (Refresh call on panels)
|
||||
* Default tenant input field in login popup
|
||||
Reference in new issue
Block a user