- Changed the way a regatta is migrated from a 'without changing boats' regatta to 'with changing boats' regatta by removing the public setter and using a special derived MigratableRegattaImpl type which provides the actual migration function

- Change COMPETITOR collection migration by renaming the old COMPETITOR collection to COMPETITOR_BAK and creating all migrated competitors in the now empty COMPETITORS collection
This commit is contained in:
Frank Mittag
2017-11-30 15:07:55 +01:00
parent 2aed2a1fee
commit b144ae9b19
11 changed files with 92 additions and 44 deletions
@@ -132,7 +132,7 @@ public interface DomainObjectFactory {
RegattaLog loadRegattaLog(RegattaLikeIdentifier identifier);
Collection<CompetitorWithBoat> loadAllLegacyCompetitorsWithBoat();
Collection<CompetitorWithBoat> renameCompetitorsCollectionAndloadAllLegacyCompetitors();
/**
* Loads all competitors (with and without embedded boats) and resolves them via the domain factory.
@@ -72,14 +72,14 @@ public enum CollectionNames {
/**
* Legacy store for competitors before implementation of bug2822
* Stores competitors with contained boats.
* Contains the old competitors with contained boats.
*/
COMPETITORS,
COMPETITORS_BAK,
/**
* Stores competitors with boat references.
* Stores competitors with or without boat references.
*/
COMPETITORS_WITH_BOAT_REFERENCES,
COMPETITORS,
/**
* Stores boats.
@@ -129,8 +129,8 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRevokeEv
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogSetCompetitorTimeOnDistanceAllowancePerNauticalMileEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogSetCompetitorTimeOnTimeFactorEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
@@ -159,6 +159,7 @@ import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl;
import com.sap.sailing.domain.base.impl.CourseDataImpl;
import com.sap.sailing.domain.base.impl.EventImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.MigratableRegattaImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl;
import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl;
@@ -1264,18 +1265,31 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
Boolean canBoatsOfCompetitorsChangePerRace = (Boolean) dbRegatta
.get(FieldNames.REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE.name());
// for backward compatibility
boolean createMigratableRegatta = false;
if (canBoatsOfCompetitorsChangePerRace == null) {
canBoatsOfCompetitorsChangePerRace = false;
createMigratableRegatta = true;
}
final RankingMetricConstructor rankingMetricConstructor = loadRankingMetricConstructor(dbRegatta);
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea,
buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
rankingMetricConstructor);
if (createMigratableRegatta) {
result = new MigratableRegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea,
buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
rankingMetricConstructor);
} else {
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea,
buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
rankingMetricConstructor);
}
result.setRegattaConfiguration(configuration);
}
return result;
@@ -2371,7 +2385,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@Override
public Collection<Competitor> loadAllCompetitors() {
ArrayList<Competitor> result = new ArrayList<>();
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
try {
for (DBObject o : collection.find()) {
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o)));
@@ -2386,9 +2400,11 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
@Override
public Collection<CompetitorWithBoat> loadAllLegacyCompetitorsWithBoat() {
public Collection<CompetitorWithBoat> renameCompetitorsCollectionAndloadAllLegacyCompetitors() {
DBCollection orginalCompetitorCollection = database.getCollection(CollectionNames.COMPETITORS.name());
orginalCompetitorCollection.rename(CollectionNames.COMPETITORS_BAK.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_BAK.name());
ArrayList<CompetitorWithBoat> result = new ArrayList<>();
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
try {
for (DBObject o : collection.find()) {
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o)));
@@ -2397,7 +2413,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load competitors.");
logger.log(Level.SEVERE, "loadLegacyCompetitors", e);
logger.log(Level.SEVERE, "renameCompetitorsCollectionAndloadAllLegacyCompetitors", e);
}
return result;
}
@@ -1352,7 +1352,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
private void storeCompetitorWithoutBoat(Competitor competitor) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
JSONObject json = competitorSerializer.serialize(competitor);
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
DBObject entry = (DBObject) JSON.parse(json.toString());
@@ -1360,7 +1360,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
private void storeCompetitorWithBoat(CompetitorWithBoat competitor) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
JSONObject json = competitorWithBoatRefSerializer.serialize(competitor);
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
DBObject entry = (DBObject) JSON.parse(json.toString());
@@ -1386,7 +1386,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
private void storeCompetitorsWithoutBoat(Iterable<Competitor> competitors) {
if (!Util.isEmpty(competitors)) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
List<DBObject> competitorsDB = new ArrayList<>();
for (Competitor competitor : competitors) {
JSONObject json = competitorSerializer.serialize(competitor);
@@ -1399,7 +1399,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
private void storeCompetitorsWithBoat(Iterable<CompetitorWithBoat> competitors) {
if (!Util.isEmpty(competitors)) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
List<DBObject> competitorsDB = new ArrayList<>();
for (CompetitorWithBoat competitor : competitors) {
JSONObject json = competitorWithBoatRefSerializer.serialize(competitor);
@@ -1413,14 +1413,14 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
@Override
public void removeAllCompetitors() {
logger.info("Removing all persistent competitors");
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
collection.drop();
}
@Override
public void removeCompetitor(Competitor competitor) {
logger.info("Removing persistent competitor info for competitor "+competitor.getName()+" with ID "+competitor.getId());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS_WITH_BOAT_REFERENCES.name());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
DBObject query = (DBObject) JSON.parse(CompetitorJsonSerializer.getCompetitorIdQuery(competitor).toString());
collection.remove(query, WriteConcern.SAFE);
}
@@ -341,10 +341,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return false;
}
@Override
public void setCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace) {
}
@Override
public void addRace(RaceDefinition race) {
}
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.DynamicPerson;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MigratableRegattaImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
@@ -693,19 +694,27 @@ public class DomainFactoryImpl implements DomainFactory {
// If the tractrac race contains boat metadata we assume the regatta can have changing boats per race.
// As the attribute 'canBoatsOfCompetitorsChangePerRace' is new and 'false' is the default value
// we need to set it's value to true for the regatta
if (competitorBoatInfo != null && trackedRegatta.getRegatta().canBoatsOfCompetitorsChangePerRace() == false) {
// we need to set this to true for the regatta to make it possible to edit the boat/competitor mappings
trackedRegatta.getRegatta().setCanBoatsOfCompetitorsChangePerRace(true);
// we need to set it's value to true for the regatta, but only if the regatta is of type MigratableRegattaImpl
Regatta regatta = trackedRegatta.getRegatta();
if (competitorBoatInfo != null && regatta.canBoatsOfCompetitorsChangePerRace() == false) {
// we need to set this to true for the regatta to make it possible to create the boat/competitor mappings
if (regatta instanceof MigratableRegattaImpl) {
MigratableRegattaImpl migratableRegatta = (MigratableRegattaImpl) regatta;
migratableRegatta.migrateCanBoatsOfCompetitorsChangePerRace(true);
logger.log(Level.INFO, "Successful migration of regatta " + regatta.getName() +
" to be of type 'canBoatsOfCompetitorsChangePerRace=true'");
} else {
logger.log(Level.SEVERE, "Regatta " + regatta.getName() +
" has wrong type 'canBoatsOfCompetitorsChangePerRace' but can't be migrated because it is not of type MigratableRegattaImpl");
}
}
// Case 1
if (trackedRegatta.getRegatta().canBoatsOfCompetitorsChangePerRace()) {
if (regatta.canBoatsOfCompetitorsChangePerRace()) {
// create an unique identifier for the boat and try to find it in the boatStore
Serializable boatId;
String sailId;
if (competitorBoatInfo != null) {
Regatta regatta = trackedRegatta.getRegatta();
LeaderboardGroup leaderboardGroup = leaderboardGroupResolver.resolveLeaderboardGroupByRegattaName(regatta.getName());
boatId = createUniqueBoatIdentifierFromBoatMetadata(regatta, leaderboardGroup, competitorBoatInfo);
sailId = competitorBoatInfo.getId(); // we take here the boatId as sailID which is a number like 1, 2, 3
@@ -146,12 +146,6 @@ public interface Regatta extends NamedWithID, IsRegattaLike, HasRaceColumnsAndRe
RegattaIdentifier getRegattaIdentifier();
/**
* Changes whether the competitors use the same boat for the whole regatta or change the boat used during the competition.
* Actually this should never be called as this should not change once set, but we need it to migrate older regattas to the new model.
*/
void setCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace);
/**
* Regattas may be constructed as implicit default regattas in which case they won't need to be stored durably and
* don't contain valuable information worth being preserved; or they are constructed explicitly with series and race
@@ -0,0 +1,30 @@
package com.sap.sailing.domain.base.impl;
import java.io.Serializable;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sse.common.TimePoint;
public class MigratableRegattaImpl extends RegattaImpl {
private static final long serialVersionUID = -3545488249832218320L;
public <S extends Series> MigratableRegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, TimePoint startDate, TimePoint endDate,
Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea,
Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) {
super(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, startDate, endDate, series,
persistent, scoringScheme, id, courseArea, buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, rankingMetricConstructor);
}
public void migrateCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace) {
super.setCanBoatsOfCompetitorsChangePerRace(canBoatsOfCompetitorsChangePerRace);
}
}
@@ -744,8 +744,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
return canBoatsOfCompetitorsChangePerRace;
}
@Override
public void setCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace) {
/**
* Changes whether the competitors use the same boat for the whole regatta or change the boat used during the competition.
* Actually this should never be called as this should not change once set, but we need it to migrate older regattas to the new model.
*/
protected void setCanBoatsOfCompetitorsChangePerRace(boolean canBoatsOfCompetitorsChangePerRace) {
this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace;
}
@@ -74,7 +74,7 @@ public class StoreAndLoadCompetitorsTest extends AbstractMongoDBTest {
private void dropCompetitorCollection() {
DB db = getMongoService().getDB();
DBCollection competitorCollection = db.getCollection(CollectionNames.COMPETITORS.name());
DBCollection competitorCollection = db.getCollection(CollectionNames.COMPETITORS_BAK.name());
competitorCollection.setWriteConcern(WriteConcern.SAFE); // ensure that the drop() has happened
competitorCollection.drop();
}
@@ -78,7 +78,7 @@ public class PersistentCompetitorStore extends TransientCompetitorStoreImpl impl
private void migrateCompetitorsIfRequired() {
boolean migrationRequired = !storeTo.getDatabase().collectionExists(CollectionNames.BOATS.name());
if (migrationRequired) {
Collection<CompetitorWithBoat> allLegacyCompetitorsWithBoat = loadFrom.loadAllLegacyCompetitorsWithBoat();
Collection<CompetitorWithBoat> allLegacyCompetitorsWithBoat = loadFrom.renameCompetitorsCollectionAndloadAllLegacyCompetitors();
List<Competitor> newCompetitors = new ArrayList<>();
List<Boat> newBoats = new ArrayList<>();