Merge branch 'master' into jbross-buoy-tender

Conflicts:
	java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
This commit is contained in:
Axel Uhl committed 2015-03-23 20:52:00 +01:00
commit 911cdeb86b
91 files changed
+1114 -720

No files matched your search

@@ -46,7 +46,7 @@ public class TestLeaderboardGroupRetrievalProcessor {
Collection<Processor<LeaderboardGroupWithContext, ?>> resultReceivers = new ArrayList<>();
resultReceivers.add(receiver);
retriever = new LeaderboardGroupRetrievalProcessor(ConcurrencyTestsUtil.getExecutor(), resultReceivers);
retriever = new LeaderboardGroupRetrievalProcessor(ConcurrencyTestsUtil.getExecutor(), resultReceivers, 0);
}
private Map<String, LeaderboardGroup> getGroupsInService() {
@@ -2,18 +2,17 @@ package com.sap.sailing.datamining.test.util;
import static org.junit.Assert.fail;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.DataMiningExecutorService;
public class ConcurrencyTestsUtil {
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
private static final ExecutorService executor = new DataMiningExecutorService(THREAD_POOL_SIZE);
public static ThreadPoolExecutor getExecutor() {
public static ExecutorService getExecutor() {
return executor;
}
@@ -16,8 +16,8 @@ public class CompetitorOfRaceInLeaderboardRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<HasLeaderboardContext, HasRaceResultOfCompetitorContext> {
public CompetitorOfRaceInLeaderboardRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasRaceResultOfCompetitorContext, ?>> resultReceivers) {
super(HasLeaderboardContext.class, HasRaceResultOfCompetitorContext.class, executor, resultReceivers);
Collection<Processor<HasRaceResultOfCompetitorContext, ?>> resultReceivers, int retrievalLevel) {
super(HasLeaderboardContext.class, HasRaceResultOfCompetitorContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -16,8 +16,8 @@ import com.sap.sse.datamining.impl.components.AbstractSimpleRetrievalProcessor;
public class GPSFixRetrievalProcessor extends AbstractSimpleRetrievalProcessor<HasTrackedLegOfCompetitorContext, HasGPSFixContext> {
public GPSFixRetrievalProcessor(ExecutorService executor, Collection<Processor<HasGPSFixContext, ?>> resultReceivers) {
super(HasTrackedLegOfCompetitorContext.class, HasGPSFixContext.class, executor, resultReceivers);
public GPSFixRetrievalProcessor(ExecutorService executor, Collection<Processor<HasGPSFixContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedLegOfCompetitorContext.class, HasGPSFixContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -14,19 +14,18 @@ public class LeaderboardGroupRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<RacingEventService, LeaderboardGroupWithContext> {
public LeaderboardGroupRetrievalProcessor(ExecutorService executor,
Collection<Processor<LeaderboardGroupWithContext, ?>> resultReceivers) {
super(RacingEventService.class, LeaderboardGroupWithContext.class, executor, resultReceivers);
Collection<Processor<LeaderboardGroupWithContext, ?>> resultReceivers, int retrievalLevel) {
super(RacingEventService.class, LeaderboardGroupWithContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
protected Iterable<LeaderboardGroupWithContext> retrieveData(RacingEventService element) {
final PolarDataService polarDataService = element.getPolarDataService();
return element
.getLeaderboardGroups()
.values()
.stream()
.map(lg -> new LeaderboardGroupWithContext(lg, polarDataService))
.collect(Collectors.toSet());
}
final PolarDataService polarDataService = element.getPolarDataService();
return element.getLeaderboardGroups()
.values()
.stream()
.map(lg -> new LeaderboardGroupWithContext(lg, polarDataService))
.collect(Collectors.toSet());
}
}
@@ -15,8 +15,8 @@ public class LeaderboardRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<LeaderboardGroupWithContext, HasLeaderboardContext> {
public LeaderboardRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasLeaderboardContext, ?>> resultReceivers) {
super(LeaderboardGroupWithContext.class, HasLeaderboardContext.class, executor, resultReceivers);
Collection<Processor<HasLeaderboardContext, ?>> resultReceivers, int retrievalLevel) {
super(LeaderboardGroupWithContext.class, HasLeaderboardContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -19,8 +19,8 @@ public class MarkPassingRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<HasTrackedRaceContext, HasMarkPassingContext> {
public MarkPassingRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasMarkPassingContext, ?>> resultReceivers) {
super(HasTrackedRaceContext.class, HasMarkPassingContext.class, executor, resultReceivers);
Collection<Processor<HasMarkPassingContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedRaceContext.class, HasMarkPassingContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -15,8 +15,8 @@ public class TrackedLegOfCompetitorRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<HasTrackedLegContext, HasTrackedLegOfCompetitorContext> {
public TrackedLegOfCompetitorRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasTrackedLegOfCompetitorContext, ?>> resultReceivers) {
super(HasTrackedLegContext.class, HasTrackedLegOfCompetitorContext.class, executor, resultReceivers);
Collection<Processor<HasTrackedLegOfCompetitorContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedLegContext.class, HasTrackedLegOfCompetitorContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -15,8 +15,8 @@ public class TrackedLegRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<HasTrackedRaceContext, HasTrackedLegContext> {
public TrackedLegRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasTrackedLegContext, ?>> resultReceivers) {
super(HasTrackedRaceContext.class, HasTrackedLegContext.class, executor, resultReceivers);
Collection<Processor<HasTrackedLegContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedRaceContext.class, HasTrackedLegContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -18,8 +18,8 @@ public class TrackedRaceRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<HasLeaderboardContext, HasTrackedRaceContext> {
public TrackedRaceRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasTrackedRaceContext, ?>> resultReceivers) {
super(HasLeaderboardContext.class, HasTrackedRaceContext.class, executor, resultReceivers);
Collection<Processor<HasTrackedRaceContext, ?>> resultReceivers, int retrievalLevel) {
super(HasLeaderboardContext.class, HasTrackedRaceContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -15,12 +15,14 @@ import java.io.Serializable;
*
*/
public enum DetailType implements Serializable {
DISTANCE_TRAVELED(0, ASCENDING), AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING), REGATTA_RANK(0, ASCENDING), OVERALL_RANK(0, ASCENDING),
DISTANCE_TRAVELED(0, ASCENDING), DISTANCE_TRAVELED_INCLUDING_GATE_START(0, ASCENDING),
AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING), REGATTA_RANK(0, ASCENDING), OVERALL_RANK(0, ASCENDING),
RANK_GAIN(0, ASCENDING), NUMBER_OF_MANEUVERS(0, ASCENDING), CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING),
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING),
SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), RACE_DISTANCE_TRAVELED(0, ASCENDING),
RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START(0, ASCENDING),
RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_GAP_TO_LEADER_IN_SECONDS(0, ASCENDING),
RACE_DISTANCE_TO_LEADER_IN_METERS(0, ASCENDING), RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING),
RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING),
@@ -19,6 +19,7 @@ public class LegEntryDTO implements Serializable {
private static final long serialVersionUID = -1236734337344886025L;
public LegType legType;
public Double distanceTraveledInMeters;
public Double distanceTraveledIncludingGateStartInMeters;
public Double averageSpeedOverGroundInKnots;
public int rank;
public Double gapToLeaderInSeconds;
@@ -62,6 +63,7 @@ public class LegEntryDTO implements Serializable {
result = prime * result
+ ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode());
result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode());
result = prime * result + ((distanceTraveledIncludingGateStartInMeters == null) ? 0 : distanceTraveledIncludingGateStartInMeters.hashCode());
result = prime
* result
+ ((estimatedTimeToNextWaypointInSeconds == null) ? 0 : estimatedTimeToNextWaypointInSeconds.hashCode());
@@ -120,6 +122,11 @@ public class LegEntryDTO implements Serializable {
return false;
} else if (!distanceTraveledInMeters.equals(other.distanceTraveledInMeters))
return false;
if (distanceTraveledIncludingGateStartInMeters == null) {
if (other.distanceTraveledIncludingGateStartInMeters != null)
return false;
} else if (!distanceTraveledIncludingGateStartInMeters.equals(other.distanceTraveledIncludingGateStartInMeters))
return false;
if (estimatedTimeToNextWaypointInSeconds == null) {
if (other.estimatedTimeToNextWaypointInSeconds != null)
return false;
@@ -12,7 +12,6 @@ import java.util.Set;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.TimingConstants;
import com.sap.sse.common.Util;
public class RaceColumnDTO extends NamedDTO implements Serializable {
@@ -25,13 +24,25 @@ public class RaceColumnDTO extends NamedDTO implements Serializable {
private double effectiveFactor;
private Map<FleetDTO, RaceLogTrackingInfoDTO> raceLogTrackingInfos;
public enum RaceColumnLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE };
public RaceColumnDTO() {
trackedRaceIdentifiersPerFleet = new HashMap<FleetDTO, RegattaAndRaceIdentifier>();
raceLogTrackingInfos = new HashMap<FleetDTO, RaceLogTrackingInfoDTO>();
racesPerFleet = new HashMap<FleetDTO, RaceDTO>();
fleets = new ArrayList<FleetDTO>();
}
public RaceColumnLiveState getLiveState(long serverTimePointAsMillis) {
RaceColumnLiveState result = RaceColumnLiveState.NOT_TRACKED;
if (hasLiveRaces(serverTimePointAsMillis) && hasGPSData() && hasWindData()) {
result = RaceColumnLiveState.TRACKED_AND_LIVE;
} else if (hasTrackedRaces()) {
result = RaceColumnLiveState.TRACKED;
}
return result;
}
public String getRaceColumnName() {
return getName();
}
@@ -144,42 +155,11 @@ public class RaceColumnDTO extends NamedDTO implements Serializable {
* point of the "live" interval as defined above.
*/
public boolean isLive(FleetDTO fleet, long serverTimePointAsMillis) {
final Date startOfLivePeriod;
final Date endOfLivePeriod;
boolean result = false;
final RaceDTO raceDTO = racesPerFleet.get(fleet);
if (raceDTO == null || raceDTO.trackedRace == null || !raceDTO.trackedRace.hasGPSData || !raceDTO.trackedRace.hasWindData) {
startOfLivePeriod = null;
endOfLivePeriod = null;
} else {
if (raceDTO.startOfRace == null) {
startOfLivePeriod = raceDTO.trackedRace.startOfTracking;
} else {
startOfLivePeriod = new Date(raceDTO.startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
}
if (raceDTO.endOfRace == null) {
if (raceDTO.trackedRace.timePointOfNewestEvent != null) {
endOfLivePeriod = new Date(raceDTO.trackedRace.timePointOfNewestEvent.getTime()
+ TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
} else {
endOfLivePeriod = null;
}
} else {
endOfLivePeriod = new Date(raceDTO.endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
}
if(raceDTO != null) {
result = raceDTO.isLive(serverTimePointAsMillis);
}
// if an empty timepoint is given then take the start of the race
if (serverTimePointAsMillis == 0) {
serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
}
// whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
// when this DTO was assembled, or there were no GPS or wind data
final boolean result =
startOfLivePeriod != null &&
endOfLivePeriod != null &&
startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
serverTimePointAsMillis <= endOfLivePeriod.getTime();
return result;
}
@@ -4,6 +4,7 @@ import java.util.Date;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.TimingConstants;
/**
* Master data about a single race that is to be transferred to the client.<p>
@@ -14,6 +15,8 @@ import com.sap.sailing.domain.common.RegattaNameAndRaceName;
public class RaceDTO extends NamedDTO {
private static final long serialVersionUID = 2613189982608149975L;
public enum RaceLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE, TRACKED_BUT_NOT_SCHEDULED };
/**
* Tells if this race is currently being tracked, meaning that a {@link RaceTracker} is
* listening for incoming GPS fixes, mark passings etc., to update a {@link TrackedRace} object
@@ -46,6 +49,20 @@ public class RaceDTO extends NamedDTO {
this.isTracked = isCurrentlyTracked;
}
public RaceLiveState getLiveState(long serverTimePointAsMillis) {
RaceLiveState result = RaceLiveState.NOT_TRACKED;
if(trackedRace != null && trackedRace.hasGPSData && trackedRace.hasWindData) {
result = RaceLiveState.TRACKED;
if(isLive(serverTimePointAsMillis)) {
result = RaceLiveState.TRACKED_AND_LIVE;
if(startOfRace == null) {
result = RaceLiveState.TRACKED_BUT_NOT_SCHEDULED;
}
}
}
return result;
}
public RegattaAndRaceIdentifier getRaceIdentifier() {
return new RegattaNameAndRaceName(regattaName, getName());
}
@@ -54,6 +71,52 @@ public class RaceDTO extends NamedDTO {
return regattaName;
}
/**
* @see {@link TrackedRace#isLive} for further explanation.
* @param serverTimePointAsMillis
* the time point (in server clock time) at which to determine whether the race is/was live
* @return <code>true</code> if <code>serverTimePointAsMillis</code> is between (inclusively) the start and end time
* point of the "live" interval as defined above.
*/
public boolean isLive(long serverTimePointAsMillis) {
final Date startOfLivePeriod;
final Date endOfLivePeriod;
if (trackedRace == null || !trackedRace.hasGPSData || !trackedRace.hasWindData) {
startOfLivePeriod = null;
endOfLivePeriod = null;
} else {
if (startOfRace == null) {
startOfLivePeriod = trackedRace.startOfTracking;
} else {
startOfLivePeriod = new Date(startOfRace.getTime() - TimingConstants.PRE_START_PHASE_DURATION_IN_MILLIS);
}
if (endOfRace == null) {
if (trackedRace.timePointOfNewestEvent != null) {
endOfLivePeriod = new Date(trackedRace.timePointOfNewestEvent.getTime()
+ TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
} else {
endOfLivePeriod = null;
}
} else {
endOfLivePeriod = new Date(endOfRace.getTime() + TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS);
}
}
// if an empty timepoint is given then take the start of the race
if (serverTimePointAsMillis == 0) {
serverTimePointAsMillis = startOfLivePeriod.getTime()+1;
}
// whenLastTrackedRaceWasLive is null if there is no tracked race for fleet, or the tracked race hasn't started yet at the server time
// when this DTO was assembled, or there were no GPS or wind data
final boolean result =
startOfLivePeriod != null &&
endOfLivePeriod != null &&
startOfLivePeriod.getTime() <= serverTimePointAsMillis &&
serverTimePointAsMillis <= endOfLivePeriod.getTime();
return result;
}
@Override
public int hashCode() {
final int prime = 31;
@@ -133,7 +133,8 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
this.cachedRacingProcedureType = determineInitialProcedureType();
this.cachedRaceStatus = RaceLogRaceStatus.UNKNOWN;
this.cachedPassId = raceLog.getCurrentPassId();
// TODO bug 2083: make sure the listener registration is "weak" in the sense that it is removed when this race state is no longer strongly referenced
// see resolved bug 2083: make sure the listener registration is "weak" in the sense that it is removed when
// this race state is no longer strongly referenced
this.raceLog.addListener(new WeakRaceLogChangedVisitor(this.raceLog, this));
// We known that recreateRacingProcedure calls update() when done, therefore this RaceState
// will be fully initialized after this line
@@ -874,14 +874,25 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override
public void attachRegattaLog(RegattaLog regattaLog) {
// TODO Auto-generated method stub
}
@Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RegattaLog fromRegattaLog) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public Boolean isGateStart() {
return null;
}
@Override
public Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
@@ -95,7 +95,6 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public RegattaAndRaceIdentifier getRaceIdentifier() {
// TODO Auto-generated method stub
return null;
}
@@ -106,13 +105,11 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public TimePoint getEndOfRace() {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterable<Util.Pair<Waypoint, Util.Pair<TimePoint, TimePoint>>> getMarkPassingsTimes() {
// TODO Auto-generated method stub
return null;
}
@@ -123,67 +120,56 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public Iterable<TrackedLeg> getTrackedLegs() {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLeg(Leg leg) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getCurrentLeg(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLegFinishingAt(Waypoint endOfLeg) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLeg getTrackedLegStartingAt(Waypoint startOfLeg) {
// TODO Auto-generated method stub
return null;
}
@Override
public GPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, Leg leg) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getUpdateCount() {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getRankDifference(Competitor competitor, Leg leg, TimePoint timePoint) {
// TODO Auto-generated method stub
return 0;
}
@@ -199,37 +185,31 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public MarkPassing getMarkPassing(Competitor competitor, Waypoint waypoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark) {
// TODO Auto-generated method stub
return null;
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Wind getWind(Position p, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public Wind getWind(Position p, TimePoint at, Set<WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
return null;
}
@@ -245,91 +225,75 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public WindTrack getOrCreateWindTrack(WindSource windSource) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation) {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitForNextUpdate(int sinceUpdate) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getEndOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfNewestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfOldestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfLastEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public long getMillisecondsOverWhichToAverageSpeed() {
// TODO Auto-generated method stub
return 0;
}
@Override
public long getMillisecondsOverWhichToAverageWind() {
// TODO Auto-generated method stub
return 0;
}
@Override
public long getDelayToLiveInMillis() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Wind getEstimatedWindDirection(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Tack getTack(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@@ -340,113 +304,91 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public Wind getDirectionFromStartToNextMark(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean raceIsKnownToStartUpwind() {
// TODO Auto-generated method stub
return false;
}
@Override
public void addListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded,
boolean notifyAboutGPSFixesAlreadyLoaded) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<WindSource> getWindSourcesToExclude() {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Set<WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindStore getWindStore() {
// TODO Auto-generated method stub
return null;
}
@Override
public Competitor getOverallLeader(TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@@ -458,223 +400,191 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitUntilLoadingFromWindStoreComplete() {
// TODO Auto-generated method stub
}
@Override
public Iterable<Mark> getMarks() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean hasWindData() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasGPSData() {
// TODO Auto-generated method stub
return false;
}
@Override
public void lockForRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public void unlockAfterRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public TrackedRaceStatus getStatus() {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitUntilNotLoading() {
// TODO Auto-generated method stub
}
@Override
public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
}
@Override
public void attachRaceLog(RaceLog raceLog) {
// TODO Auto-generated method stub
}
@Override
public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
return null;
}
@Override
public void addCourseDesignChangedListener(CourseDesignChangedListener listener) {
// TODO Auto-generated method stub
}
@Override
public Distance getDistanceToStartLine(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceFromStarboardSideOfStartLineWhenPassingStart(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public boolean isLive(TimePoint at) {
// TODO Auto-generated method stub
return false;
}
@Override
public Iterable<Sideline> getCourseSidelines() {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceToStartLine(Competitor competitor, long millisecondsBeforeRaceStart) {
// TODO Auto-generated method stub
return null;
}
@Override
public Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart) {
// TODO Auto-generated method stub
return null;
}
public void addStartTimeChangedListener(StartTimeChangedListener listener) {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
@Override
public LineDetails getStartLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public LineDetails getFinishLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getCourseLength() {
// TODO Auto-generated method stub
return null;
}
@Override
public Speed getSpeedWhenCrossingStartLine(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceFromStarboardSideOfStartLine(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public GPSFixStore getGPSFixStore() {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RaceLog forRaceLog) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public void addRaceAbortedListener(RaceAbortedListener listener) {
// TODO Auto-generated method stub
}
@Override
public Position getCenterOfCourse(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public void attachRegattaLog(RegattaLog regattaLog) {
// TODO Auto-generated method stub
}
@Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RegattaLog fromRegattaLog) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public Boolean isGateStart() {
return null;
}
@Override
public Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
@@ -1634,6 +1634,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots;
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
result.distanceTraveledInMeters = distanceTraveled == null ? null : distanceTraveled.getMeters();
Distance distanceTraveledConsideringGateStart = trackedLeg.getDistanceTraveledConsideringGateStart(timePoint);
result.distanceTraveledIncludingGateStartInMeters = distanceTraveledConsideringGateStart == null ? null : distanceTraveledConsideringGateStart.getMeters();
result.estimatedTimeToNextWaypointInSeconds = trackedLeg.getEstimatedTimeToNextMarkInSeconds(timePoint, WindPositionMode.EXACT, cache);
result.timeInMilliseconds = time.asMillis();
result.finished = trackedLeg.hasFinishedLeg(timePoint);
@@ -39,6 +39,20 @@ public interface TrackedLegOfCompetitor extends Serializable {
*/
Distance getDistanceTraveled(TimePoint timePoint);
/**
* When a race uses a gate start, competitors are free to choose their start time point within the gate opening
* time. During this time a pathfinder boat, also called "rabbit," progresses on port tack until the gate launch
* time is over. After the gate launch time, competitors may still start until the gate closes, but that's usually
* not a useful option because starting after the gate launch time usually means losing time towards the next mark.
* <p>
*
* Depending on when a competitor starts, they will have different distances to sail: early starters a bit more,
* late starters a bit less. To normalize and make comparable, for the first leg of a gate start race, this method
* adds the distance between the competitor and the port side of the start line at the time point when the competitor
* starts.
*/
Distance getDistanceTraveledConsideringGateStart(TimePoint timePoint);
/**
* Estimates how much the competitor still has to go to the end waypoint of this leg, projected onto the wind
* direction. If the competitor already finished this leg, a zero, non-<code>null</code> distance will result.
@@ -7,6 +7,7 @@ import java.util.Set;
import java.util.SortedSet;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.shared.events.DeviceMappingEvent;
import com.sap.sailing.domain.base.Competitor;
@@ -31,6 +32,7 @@ import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -83,7 +85,6 @@ public interface TrackedRace extends Serializable {
* <li>Returns time of the last mark passing recorded for the finish line</li>
* <li>TODO: Returns the time of the first passing of the finish line + the target window (defined in the
* competition rules) if a target window has been defined for the race</li>
* </ol>
*/
TimePoint getEndOfRace();
@@ -478,6 +479,11 @@ public interface TrackedRace extends Serializable {
*/
Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint);
/**
* See {@link TrackedLegOfCompetitor#getDistanceTraveledConsideringGateStart(TimePoint)}
*/
Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint);
Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
/**
@@ -725,4 +731,19 @@ public interface TrackedRace extends Serializable {
*/
Position getCenterOfCourse(TimePoint at);
/**
* If the {@link RacingProcedure} defined by any of the {@link #attachedRaceLogs attached} {@link RaceLog}s
* has type {@link RacingProcedureType#GateStart}, this method returns <code>true</code>, <code>false</code> for
* any other type found. If no type is found, e.g., because no race log is currently attached to this tracked race,
* <code>null</code> is returned, meaning that the type is not known.
*/
Boolean isGateStart();
/**
* If the race was started with a gate start (see {@link #isGateStart()}, this method returns the distance between
* the competitor's starting position and the port side of the start line (pin end); otherwise, returns a zero
* distance.
*/
Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint);
}
@@ -10,6 +10,7 @@ import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
@@ -110,6 +111,19 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
return getTrackedRace().getTrack(getCompetitor()).getDistanceTraveled(legStart.getTimePoint(), end);
}
}
@Override
public Distance getDistanceTraveledConsideringGateStart(TimePoint timePoint) {
final Distance result;
final Distance preResult = getDistanceTraveled(timePoint);
final Waypoint from = getLeg().getFrom();
if (from == getTrackedRace().getRace().getCourse().getFirstWaypoint()) {
result = preResult.add(getTrackedRace().getAdditionalGateStartDistance(getCompetitor(), timePoint));
} else {
result = preResult;
}
return result;
}
private MarkPassing getMarkPassingForLegStart() {
MarkPassing legStart = getTrackedRace().getMarkPassing(getCompetitor(), getLeg().getFrom());
@@ -35,6 +35,9 @@ import com.sap.sailing.domain.abstractlog.AbstractLog;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.ReadonlyRacingProcedure;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
@@ -84,6 +87,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
@@ -888,8 +892,17 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
}
@Override
public Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint) {
return getDistanceTraveled(competitor, timePoint, /* consider gate start */ true);
}
@Override
public Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint) {
return getDistanceTraveled(competitor, timePoint, /* consider gate start */ false);
}
private Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint, boolean considerGateStart) {
final Distance result;
NavigableSet<MarkPassing> markPassings = getMarkPassings(competitor);
try {
@@ -898,22 +911,29 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
result = null;
} else {
TimePoint end = timePoint;
final TrackedLegOfCompetitor trackedLegOfCompetitor;
if (markPassings.last().getWaypoint() == getRace().getCourse().getLastWaypoint()
&& timePoint.compareTo(markPassings.last().getTimePoint()) > 0) {
// competitor has finished race; use time point of crossing the finish line
// competitor has finished race at or before the requested time point; use time point of crossing the finish line
end = markPassings.last().getTimePoint();
} else if (markPassings.last().getWaypoint() != getRace().getCourse().getLastWaypoint()
} else if ((trackedLegOfCompetitor=getTrackedLeg(competitor, timePoint)) == null ||
(!trackedLegOfCompetitor.hasFinishedLeg(timePoint)
&& ((getEndOfTracking() != null && timePoint.after(getEndOfTracking()))
|| getStatus().getStatus() == TrackedRaceStatusEnum.FINISHED)) {
|| getStatus().getStatus() == TrackedRaceStatusEnum.FINISHED))) {
// If the race is no longer tracking and hence no more data can be expected, and the competitor
// hasn't finished the race, no valid distance traveled can be determined
// for the competitor in this race.
// hasn't finished a leg after the requested time point, no valid distance traveled can be determined
// for the competitor in this race the the time point requested
end = null;
}
if (end == null) {
result = null;
} else {
result = getTrack(competitor).getDistanceTraveled(markPassings.first().getTimePoint(), end);
final Distance preResult = getTrack(competitor).getDistanceTraveled(markPassings.first().getTimePoint(), end);
if (considerGateStart && preResult != null) {
result = preResult.add(getAdditionalGateStartDistance(competitor, timePoint));
} else {
result = preResult;
}
}
}
return result;
@@ -3287,5 +3307,36 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
Iterable<Waypoint> getWaypoints() {
return markPassingsForWaypoint.keySet();
}
@Override
public Boolean isGateStart() {
Boolean result = null;
for (RaceLog raceLog : attachedRaceLogs.values()) {
ReadonlyRaceState raceState = RaceStateImpl.create(raceLog);
ReadonlyRacingProcedure procedure = raceState.getRacingProcedure();
if (procedure != null && procedure.getType() != null) {
result = procedure.getType() == RacingProcedureType.GateStart;
break;
}
}
return result;
}
@Override
public Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint) {
final Distance result;
final Leg startLeg = getRace().getCourse().getFirstLeg();
final TrackedLegOfCompetitor competitorLeg;
if (startLeg != null && isGateStart() == true && (competitorLeg=getTrackedLeg(competitor, startLeg)).hasStartedLeg(timePoint)) {
TimePoint competitorLegStartTime = competitorLeg.getStartTime();
final Mark portMarkOfStartLine = getStartLine(competitorLegStartTime).getPortMarkWhileApproachingLine();
final Position portSideOfStartLinePosition = getOrCreateTrack(portMarkOfStartLine)
.getEstimatedPosition(competitorLegStartTime, /* extrapolate */true);
result = portSideOfStartLinePosition.getDistance(getTrack(competitor).getEstimatedPosition(competitorLegStartTime, /* extrapolate */false));
} else {
result = Distance.NULL;
}
return result;
}
}
@@ -6,6 +6,7 @@ import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.UIObject;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO.RaceColumnLiveState;
import com.sap.sse.gwt.client.player.Timer;
public class RegattaPhaseRace extends UIObject {
@@ -14,7 +15,7 @@ public class RegattaPhaseRace extends UIObject {
interface RegattaPhaseRaceUiBinder extends UiBinder<DivElement, RegattaPhaseRace> {
}
@UiField DivElement raceStatus;
@UiField DivElement raceColumnStatus;
private enum DataStatusEnum {
FINISHED ("finished"), LIVE("live"), SCHEDULED("scheduled");
@@ -31,14 +32,20 @@ public class RegattaPhaseRace extends UIObject {
RegattaResources.INSTANCE.css().ensureInjected();
setElement(uiBinder.createAndBindUi(this));
DataStatusEnum dataStatus;
if(raceColumn.hasLiveRaces(timerForClientServerOffset.getLiveTimePointInMillis())) {
dataStatus = DataStatusEnum.LIVE;
} else if (raceColumn.hasTrackedRaces()) {
dataStatus = DataStatusEnum.FINISHED;
} else {
dataStatus = DataStatusEnum.SCHEDULED;
DataStatusEnum dataStatus = DataStatusEnum.SCHEDULED;
RaceColumnLiveState liveState = raceColumn.getLiveState(timerForClientServerOffset.getLiveTimePointInMillis());
switch(liveState) {
case NOT_TRACKED:
dataStatus = DataStatusEnum.SCHEDULED;
break;
case TRACKED:
dataStatus = DataStatusEnum.FINISHED;
break;
case TRACKED_AND_LIVE:
dataStatus = DataStatusEnum.LIVE;
break;
}
raceStatus.setAttribute(DataStatusEnum.ATTRIBUTE_NAME, dataStatus.attributeValue);
raceColumnStatus.setAttribute(DataStatusEnum.ATTRIBUTE_NAME, dataStatus.attributeValue);
}
}
@@ -5,5 +5,5 @@
<ui:with field="i18n" type="com.sap.sailing.gwt.common.client.i18n.TextMessages"/>
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.client.place.event.regatta.RegattaResources" />
<div ui:field="raceStatus" class="{local_res.css.regatta_intro_phases_phase_races_race}"></div>
<div ui:field="raceColumnStatus" class="{local_res.css.regatta_intro_phases_phase_races_race}"></div>
</ui:UiBinder>
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.dto.RaceDTO.RaceLiveState;
import com.sap.sailing.gwt.home.client.place.event.EventPlaceNavigator;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.gwt.client.player.Timer;
@@ -23,8 +24,6 @@ import com.sap.sse.gwt.client.player.Timer;
public class EventRegattaRacesRace extends Composite {
private static EventRegattaRacesRaceUiBinder uiBinder = GWT.create(EventRegattaRacesRaceUiBinder.class);
private enum SimpleRaceStates { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE, TRACKED_BUT_NOT_SCHEDULED };
interface EventRegattaRacesRaceUiBinder extends UiBinder<Widget, EventRegattaRacesRace> {
}
@@ -62,16 +61,12 @@ public class EventRegattaRacesRace extends Composite {
private final DateTimeFormat raceTimeFormat = DateTimeFormat.getFormat("EEE, h:mm a");
private final FleetDTO fleet;
private final RaceColumnDTO raceColumn;
private final RaceDTO race;
private final Timer timerForClientServerOffset;
private Element[] allConditionalElements;
public EventRegattaRacesRace(StrippedLeaderboardDTO leaderboard, FleetDTO fleet, RaceColumnDTO raceColumn, Timer timerForClientServerOffset, EventPlaceNavigator pageNavigator) {
this.fleet = fleet;
this.raceColumn = raceColumn;
this.timerForClientServerOffset = timerForClientServerOffset;
race = raceColumn.getRace(fleet);
@@ -97,25 +92,6 @@ public class EventRegattaRacesRace extends Composite {
updateUI();
}
private SimpleRaceStates getSimpleRaceState() {
SimpleRaceStates simpleRaceState = SimpleRaceStates.NOT_TRACKED;
if(race != null && race.trackedRace != null && race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
simpleRaceState = SimpleRaceStates.TRACKED;
if(isLive()) {
simpleRaceState = SimpleRaceStates.TRACKED_AND_LIVE;
if(race.startOfRace == null) {
simpleRaceState = SimpleRaceStates.TRACKED_BUT_NOT_SCHEDULED;
}
}
}
return simpleRaceState;
}
private boolean isLive() {
return raceColumn.isLive(fleet, timerForClientServerOffset.getLiveTimePointInMillis());
}
private void updateUI() {
averageRaceWind.setInnerText("tbd.");
@@ -123,7 +99,8 @@ public class EventRegattaRacesRace extends Composite {
hideElement(el);
}
switch(getSimpleRaceState()) {
RaceLiveState liveState = race != null ? race.getLiveState(timerForClientServerOffset.getLiveTimePointInMillis()) : RaceLiveState.NOT_TRACKED;
switch(liveState) {
case NOT_TRACKED:
showElement(raceNotTrackedDiv);
break;
@@ -32,7 +32,7 @@ public class CompetitorTableWrapper<S extends SelectionModel<CompetitorDTO>> ext
public CompetitorTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,ErrorReporter errorReporter,
boolean multiSelection, boolean enablePager) {
super(sailingService, stringMessages, errorReporter, multiSelection, enablePager);
ListHandler<CompetitorDTO> competitorColumnListHandler = new ListHandler<CompetitorDTO>(dataProvider.getList());
ListHandler<CompetitorDTO> competitorColumnListHandler = getColumnSortHandler();
// competitors table
TextColumn<CompetitorDTO> competitorNameColumn = new TextColumn<CompetitorDTO>() {
@@ -34,7 +34,7 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public abstract class CourseManagementWidget implements IsWidget {
protected final MarkTableWrapper<MultiSelectionModel<MarkDTO>> marks;
protected final ControlPointTableWrapper<MultiSelectionModel<ControlPointDTO>> multiMarkControlPoints;
protected final ControlPointTableWrapper<SingleSelectionModel<ControlPointDTO>> multiMarkControlPoints;
protected final WaypointTableWrapper<SingleSelectionModel<WaypointDTO>> waypoints;
protected final Grid mainPanel;
@@ -70,8 +70,8 @@ public abstract class CourseManagementWidget implements IsWidget {
waypoints = new WaypointTableWrapper<SingleSelectionModel<WaypointDTO>>(
/* multiSelection */ false, sailingService, stringMessages, errorReporter);
multiMarkControlPoints = new ControlPointTableWrapper<MultiSelectionModel<ControlPointDTO>>(
/* multiSelection */ true, sailingService, stringMessages, errorReporter);
multiMarkControlPoints = new ControlPointTableWrapper<SingleSelectionModel<ControlPointDTO>>(
/* multiSelection */ false, sailingService, stringMessages, errorReporter);
marks = new MarkTableWrapper<MultiSelectionModel<MarkDTO>>(
/* multiSelection */ true, sailingService, stringMessages, errorReporter);
@@ -31,7 +31,6 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
public DeviceMappingTableWrapper(SailingServiceAsync sailingService, final StringMessages stringMessages,
ErrorReporter errorReporter) {
super(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true);
showPingMappingsCb = new CheckBox(stringMessages.showPingMarkMappings());
showPingMappingsCb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
@@ -40,12 +39,9 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
}
});
mainPanel.insert(showPingMappingsCb, 0);
table.setWidth("1000px", true);
table.addStyleName("wrap-cols");
ListHandler<DeviceMappingDTO> listHandler = new ListHandler<DeviceMappingDTO>(dataProvider.getList());
ListHandler<DeviceMappingDTO> listHandler = getColumnSortHandler();
TextColumn<DeviceMappingDTO> itemTypeCol = new TextColumn<DeviceMappingDTO>() {
@Override
public String getValue(DeviceMappingDTO mapping) {
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -10,6 +11,7 @@ import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.view.client.SelectionModel;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.impl.NaturalComparator;
import com.sap.sailing.gwt.ui.adminconsole.AbstractLeaderboardConfigPanel.RaceColumnDTOAndFleetDTOWithNameBasedEquality;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardConfigPanel.AnchorCell;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardConfigPanel.AnchorTemplates;
@@ -45,14 +47,26 @@ extends TableWrapper<RaceColumnDTOAndFleetDTOWithNameBasedEquality, S> {
}
}
};
raceNameColumn.setSortable(true);
getColumnSortHandler().setComparator(raceNameColumn, new Comparator<RaceColumnDTOAndFleetDTOWithNameBasedEquality>() {
@Override
public int compare(RaceColumnDTOAndFleetDTOWithNameBasedEquality o1, RaceColumnDTOAndFleetDTOWithNameBasedEquality o2) {
return new NaturalComparator().compare(o1.getA().getName(), o2.getA().getName());
}
});
TextColumn<RaceColumnDTOAndFleetDTOWithNameBasedEquality> fleetNameColumn = new TextColumn<RaceColumnDTOAndFleetDTOWithNameBasedEquality>() {
@Override
public String getValue(RaceColumnDTOAndFleetDTOWithNameBasedEquality object) {
return object.getB().getName();
}
};
fleetNameColumn.setSortable(true);
getColumnSortHandler().setComparator(fleetNameColumn, new Comparator<RaceColumnDTOAndFleetDTOWithNameBasedEquality>() {
@Override
public int compare(RaceColumnDTOAndFleetDTOWithNameBasedEquality o1, RaceColumnDTOAndFleetDTOWithNameBasedEquality o2) {
return new NaturalComparator().compare(o1.getB().getName(), o2.getB().getName());
}
});
table.addColumn(raceNameColumn, stringMessages.race());
table.addColumn(fleetNameColumn, stringMessages.fleet());
}
@@ -2,6 +2,7 @@ package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.SimplePager;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -16,7 +17,9 @@ import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.ErrorReporter;
/**
* Implementing classes still have to add the table to the main panel.
* Implementing classes still have to add the table to the main panel. The table created and wrapped by this object
* offers already a {@link ListHandler} for sorting. Subclasses can obtain the table's default column sort handler
* created by this class's constructor by calling {@link #getColumnSortHandler}.
*/
public abstract class TableWrapper<T, S extends SelectionModel<T>> implements IsWidget {
protected final CellTable<T> table;
@@ -27,6 +30,7 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
protected final ErrorReporter errorReporter;
private final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
private final ListHandler<T> columnSortHandler;
@Override
public Widget asWidget() {
@@ -38,6 +42,9 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
this.sailingService = sailingService;
this.errorReporter = errorReporter;
table = new CellTable<T>(10000, tableRes);
this.dataProvider = new ListDataProvider<T>();
this.columnSortHandler = new ListHandler<T>(dataProvider.getList());
table.addColumnSortHandler(this.columnSortHandler);
if (multiSelection) {
SelectionCheckboxColumn<T> selectionCheckboxColumn = new SelectionCheckboxColumn<T>(
tableRes.cellTableStyle().cellTableCheckboxSelected(),
@@ -48,6 +55,7 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
return dataProvider;
}
};
columnSortHandler.setComparator(selectionCheckboxColumn, selectionCheckboxColumn.getComparator());
@SuppressWarnings("unchecked")
S typedSelectionModel = (S) selectionCheckboxColumn.getSelectionModel();
selectionModel = typedSelectionModel;
@@ -59,7 +67,6 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
selectionModel = typedSelectionModel;
table.setSelectionModel(selectionModel);
}
this.dataProvider = new ListDataProvider<T>();
mainPanel = new VerticalPanel();
dataProvider.addDataDisplay(table);
mainPanel.add(table);
@@ -71,6 +78,10 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
}
}
public ListHandler<T> getColumnSortHandler() {
return columnSortHandler;
}
public CellTable<T> getTable() {
return table;
}
@@ -14,6 +14,8 @@ public class DetailTypeFormatter {
switch (detailType) {
case DISTANCE_TRAVELED:
return stringMessages.distanceInMeters();
case DISTANCE_TRAVELED_INCLUDING_GATE_START:
return stringMessages.distanceIncludingGateStartInMeters();
case AVERAGE_SPEED_OVER_GROUND_IN_KNOTS:
return stringMessages.averageSpeedInKnots();
case RANK_GAIN:
@@ -50,6 +52,8 @@ public class DetailTypeFormatter {
return stringMessages.windwardDistanceToGoInMeters();
case RACE_DISTANCE_TRAVELED:
return stringMessages.distanceInMeters();
case RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START:
return stringMessages.distanceIncludingGateStartInMeters();
case RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS:
return stringMessages.averageSpeedInKnots();
case RACE_GAP_TO_LEADER_IN_SECONDS:
@@ -159,8 +163,10 @@ public class DetailTypeFormatter {
case WINDWARD_DISTANCE_TO_OVERALL_LEADER:
case WINDWARD_DISTANCE_TO_GO_IN_METERS:
case DISTANCE_TRAVELED:
case DISTANCE_TRAVELED_INCLUDING_GATE_START:
case RACE_DISTANCE_TO_LEADER_IN_METERS:
case RACE_DISTANCE_TRAVELED:
case RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START:
case AVERAGE_TACK_LOSS_IN_METERS:
case AVERAGE_JIBE_LOSS_IN_METERS:
case AVERAGE_MANEUVER_LOSS_IN_METERS:
@@ -257,6 +263,8 @@ public class DetailTypeFormatter {
return "";
case DISTANCE_TRAVELED:
return stringMessages.distanceTraveledTooltip();
case DISTANCE_TRAVELED_INCLUDING_GATE_START:
return stringMessages.distanceTraveledIncludingGateStartTooltip();
case AVERAGE_SPEED_OVER_GROUND_IN_KNOTS:
return stringMessages.averageSpeedInKnotsTooltip();
case ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS:
@@ -295,6 +303,8 @@ public class DetailTypeFormatter {
return stringMessages.startTackTooltip();
case RACE_DISTANCE_TRAVELED:
return stringMessages.raceDistanceTraveledTooltip();
case RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START:
return stringMessages.raceDistanceTraveledIncludingGateStartTooltip();
case RACE_GAP_TO_LEADER_IN_SECONDS:
return stringMessages.gapToLeaderInSecondsTooltip();
case RACE_RANK:
@@ -1165,6 +1165,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String orMultipleEmails();
String courseOverGroundTrueDegreesTooltip();
String courseOverGroundTrueDegrees();
String distanceIncludingGateStartInMeters();
String distanceTraveledIncludingGateStartTooltip();
String raceDistanceTraveledIncludingGateStartTooltip();
String results();
String group();
String valueAscending();
@@ -1145,6 +1145,9 @@ inviteBuoyTenders=Invite Buoy Tenders
orMultipleEmails=or multiple E-Mails separated by comma
courseOverGroundTrueDegreesTooltip=True course over ground in degrees
courseOverGroundTrueDegrees=COG
distanceIncludingGateStartInMeters=Distance (w/ Gate Start)
distanceTraveledIncludingGateStartTooltip=The distance traveled from the leg start to the end\nor to the current time point, if the leg isn''t finished.\nIf the leg includes a gate start, the distance from the pin end to the starting position is included\nto make competitors comparable even when started at different times.
raceDistanceTraveledIncludingGateStartTooltip=The distance traveled from the race start to the end\nor to the current time point, if the race isn''t finished. For a gate start, the distance from the pin end to the starting position is included\nto make competitors comparable even when started at different times.
results=Results
group=Group
valueAscending=Value (Ascending)
@@ -165,7 +165,7 @@ chooseChart=Diagramm wählen
raceNameEmpty=Rennname leer
distanceTraveled=Zurückgelegte Strecke
distanceTraveledTooltip=Die zurückgelegt Strecke vom Start des Schenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Schenkel noch nicht beendet ist.
raceDistanceTraveledTooltip=Die zurückgelegt Strecke vom Start des Rennens, bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls das Rennen noch nicht beendet ist.
raceDistanceTraveledTooltip=Die zurückgelegte Strecke vom Start des Rennens, bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls das Rennen noch nicht beendet ist.
velocityMadeGoodLong=Velocity Made Good
speedOverGroundLong=Speed Over Ground
gapToLeaderLong=Abstand zum führenden Boot
@@ -1135,6 +1135,9 @@ inviteBuoyTenders=Tonnenleger einladen
orMultipleEmails=oder mehrere E-Mails (Komma-separiert)
courseOverGroundTrueDegreesTooltip=Rechtweisender Kurs über Grund in Grad
courseOverGroundTrueDegrees=KüG
distanceIncludingGateStartInMeters=Distanz (m. Torstart)
distanceTraveledIncludingGateStartTooltip=Die zurückgelegt Strecke vom Start des Schenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Schenkel noch nicht beendet ist.\nEnthält der Schenkel einen Torstart, so wird die Distanz vom Pin End bis zur Startposition des Teilnehmers hinzugezählt,\num so vergleichbare Distanzen auch für Teilneher zu erhalten, die zu verschiedenen Zeiten gestartet sind.
raceDistanceTraveledIncludingGateStartTooltip=Die zurückgelegte Strecke vom Start des Rennens, bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls das Rennen noch nicht beendet ist. Bei einem Torstart wird die Distanz vom Pin End bis zur Startposition des Teilnehmers hinzugezählt,\num so vergleichbare Distanzen auch für Teilneher zu erhalten, die zu verschiedenen Zeiten gestartet sind.
results=Ergebnisse
group=Gruppe
valueAscending=Wert (Aufsteigend)
@@ -1084,6 +1084,9 @@ inviteBuoyTenders=Invite Buoy Tenders
orMultipleEmails=or multiple E-Mails separated by comma
courseOverGroundTrueDegreesTooltip=True course over ground in degrees
courseOverGroundTrueDegrees=COG
distanceIncludingGateStartInMeters=Distance (w/ Gate Start)
distanceTraveledIncludingGateStartTooltip=The distance traveled from the leg start to the end\nor to the current time point, if the leg isn''t finished.\nIf the leg includes a gate start, the distance from the pin end to the starting position is included\nto make competitors comparable even when started at different times.
raceDistanceTraveledIncludingGateStartTooltip=The distance traveled from the race start to the end\nor to the current time point, if the race isn''t finished. For a gate start, the distance from the pin end to the starting position is included\nto make competitors comparable even when started at different times.
results=Results
group=Group
valueAscending=Value (Ascending)
@@ -27,6 +27,7 @@ public class MultiCompetitorRaceChartSettingsComponent extends AbstractChartSett
availableDetailsTypes = new ArrayList<DetailType>();
availableDetailsTypes.add(DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER);
availableDetailsTypes.add(DetailType.DISTANCE_TRAVELED);
availableDetailsTypes.add(DetailType.DISTANCE_TRAVELED_INCLUDING_GATE_START);
availableDetailsTypes.add(DetailType.VELOCITY_MADE_GOOD_IN_KNOTS);
availableDetailsTypes.add(DetailType.GAP_TO_LEADER_IN_SECONDS);
availableDetailsTypes.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
@@ -49,11 +49,11 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint {
SplitLayoutPanel splitPanel = new SplitLayoutPanel();
rootPanel.add(splitPanel);
DockLayoutPanel dockPanel = new DockLayoutPanel(Unit.PX);
dockPanel.addNorth(createLogoAndTitlePanel(), 68);
DockLayoutPanel selectionDockPanel = new DockLayoutPanel(Unit.PX);
selectionDockPanel.addNorth(createLogoAndTitlePanel(), 68);
BufferingQueryDefinitionProviderWithControls queryDefinitionProviderWithControls = new BufferingQueryDefinitionProviderWithControls(session, getStringMessages(), sailingService, dataMiningService, this);
queryDefinitionProviderWithControls.getEntryWidget().addStyleName("dataMiningPanel");
dockPanel.add(queryDefinitionProviderWithControls.getEntryWidget());
selectionDockPanel.add(queryDefinitionProviderWithControls.getEntryWidget());
ResultsPresenter<Number> resultsPresenter = new ResultsChart(getStringMessages());
if (GwtHttpRequestUtils.getBooleanParameter("benchmark", false)) {
@@ -63,7 +63,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint {
splitPanel.addSouth(resultsPresenter.getWidget(), 400);
}
splitPanel.add(dockPanel);
splitPanel.add(selectionDockPanel);
QueryRunner queryRunner = new SimpleQueryRunner(session, getStringMessages(), dataMiningService, this, queryDefinitionProviderWithControls, resultsPresenter);
queryDefinitionProviderWithControls.addControl(queryRunner.getEntryWidget());
@@ -22,17 +22,17 @@ import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData;
import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter;
import org.moxieapps.gwt.highcharts.client.labels.YAxisLabels;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.text.shared.AbstractRenderer;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ValueListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.panels.ResizingFlowPanel;
import com.sap.sailing.gwt.ui.client.shared.panels.ResizingSimplePanel;
import com.sap.sailing.gwt.ui.datamining.ResultsPresenter;
import com.sap.sse.datamining.shared.GroupKey;
@@ -76,7 +76,7 @@ public class ResultsChart implements ResultsPresenter<Number> {
};
private final StringMessages stringMessages;
private final FlowPanel mainPanel;
private final DockLayoutPanel mainPanel;
private final HorizontalPanel sortByPanel;
private final ValueListBox<Comparator<GroupKey>> keyComparatorListBox;
@@ -95,17 +95,12 @@ public class ResultsChart implements ResultsPresenter<Number> {
super();
this.stringMessages = stringMessages;
mainPanel = new ResizingFlowPanel() {
@Override
public void onResize() {
presentationPanel.onResize();
}
};
mainPanel = new DockLayoutPanel(Unit.PX);
sortByPanel = new HorizontalPanel();
sortByPanel.setSpacing(5);
sortByPanel.setVisible(false);
mainPanel.add(sortByPanel);
mainPanel.addNorth(sortByPanel, 50);
sortByPanel.add(new Label(stringMessages.sortBy()));
keyComparatorListBox = new ValueListBox<>(new AbstractRenderer<Comparator<?>>() {
@Override
@@ -810,6 +810,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
if (isShowAddedScores()) {
double o1AddedScore = computeAddedScores(o1);
double o2AddedScore = computeAddedScores(o2);
// FIXME bug 2717: need to respect the scoring scheme's isHigherBetter() flag here
double result = o1AddedScore == 0. ? o2AddedScore == 0. ? 0. : isAscending() ? 1. : -1. : o2AddedScore == 0. ? isAscending() ? 1. : -1. : o2AddedScore - o1AddedScore;
return result > 0 ? 1 : result < 0 ? -1 : 0;
} else {
@@ -840,7 +841,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
public static DetailType[] getAvailableRaceDetailColumnTypes() {
return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS,
DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.RACE_DISTANCE_TRAVELED,
DetailType.RACE_DISTANCE_TRAVELED, DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START,
DetailType.RACE_TIME_TRAVELED,
DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS,
DetailType.NUMBER_OF_MANEUVERS, DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG,
@@ -903,6 +904,9 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
result.put(DetailType.RACE_DISTANCE_TRAVELED,
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED, new RaceDistanceTraveledInMeters(),
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
result.put(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START,
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, new RaceDistanceTraveledIncludingGateStartInMeters(),
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
result.put(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, new RaceAverageSpeedInKnots(),
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
@@ -1254,6 +1258,35 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
}
}
/**
* Accumulates the distance traveled over all legs of a race and considers the specifics of a gate start. The
* first leg of a gate start gets as additional distance traveled the distance from the port side of the line to
* the point where the competitor started which helps normalize the distance traveled and make them comparable
* across early and late starters.
*
* @author Axel Uhl (D043530)
*/
private class RaceDistanceTraveledIncludingGateStartInMeters implements LegDetailField<Double> {
@Override
public Double get(LeaderboardRowDTO row) {
Double result = null;
LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName());
if (fieldsForRace != null && fieldsForRace.legDetails != null) {
for (LegEntryDTO legDetail : fieldsForRace.legDetails) {
if (legDetail != null) {
if (legDetail.distanceTraveledIncludingGateStartInMeters != null) {
if (result == null) {
result = 0.0;
}
result += legDetail.distanceTraveledIncludingGateStartInMeters;
}
}
}
}
return result;
}
}
private class CurrentLeg implements LegDetailField<Double> {
@Override
public Double get(LeaderboardRowDTO row) {
@@ -54,6 +54,13 @@ public class LegColumn extends ExpandableSortableColumn<String> {
}
}
private class DistanceTraveledIncludingGateStartInMeters extends AbstractLegDetailField<Double> {
@Override
protected Double getFromNonNullEntry(LegEntryDTO entry) {
return entry.distanceTraveledIncludingGateStartInMeters;
}
}
private class TimeTraveledInSeconds extends AbstractLegDetailField<Double> {
@Override
protected Double getFromNonNullEntry(LegEntryDTO entry) {
@@ -234,6 +241,7 @@ public class LegColumn extends ExpandableSortableColumn<String> {
public static DetailType[] getAvailableLegDetailColumnTypes() {
return new DetailType[] { DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.DISTANCE_TRAVELED,
DetailType.DISTANCE_TRAVELED_INCLUDING_GATE_START,
DetailType.GAP_TO_LEADER_IN_SECONDS, DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS,
DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED, DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
DetailType.WINDWARD_DISTANCE_TO_GO_IN_METERS, DetailType.NUMBER_OF_MANEUVERS,
@@ -251,6 +259,9 @@ public class LegColumn extends ExpandableSortableColumn<String> {
result.put(DetailType.DISTANCE_TRAVELED,
new FormattedDoubleDetailTypeColumn(DetailType.DISTANCE_TRAVELED, new DistanceTraveledInMeters(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
result.put(DetailType.DISTANCE_TRAVELED_INCLUDING_GATE_START,
new FormattedDoubleDetailTypeColumn(DetailType.DISTANCE_TRAVELED_INCLUDING_GATE_START, new DistanceTraveledIncludingGateStartInMeters(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
result.put(DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
new FormattedDoubleDetailTypeColumn(DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, new AverageSpeedOverGroundInKnots(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
@@ -129,6 +129,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM
Iterable<Function<?>> dimensions = functionDTOsAsFunctions(dimensionDTOs);
Locale locale = ResourceBundleStringMessages.Util.getLocaleFor(localeInfoName);
Query<Set<Object>> dimensionValuesQuery = getDataMiningServer().createDimensionValuesQuery(retrieverChainDefinition, retrieverLevel, dimensions, locale);
// QueryResult<Set<Object>> result = getDataMiningServer().runNewQueryAndAbortPreviousQueries(session, dimensionValuesQuery);
QueryResult<Set<Object>> result = dimensionValuesQuery.run();
return result;
}
@@ -2746,6 +2746,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
result = distanceTraveled == null ? null : distanceTraveled.getMeters();
}
break;
case DISTANCE_TRAVELED_INCLUDING_GATE_START:
if (trackedLeg != null) {
Distance distanceTraveledConsideringGateStart = trackedRace.getDistanceTraveledIncludingGateStart(competitor, timePoint);
result = distanceTraveledConsideringGateStart == null ? null : distanceTraveledConsideringGateStart.getMeters();
}
break;
case GAP_TO_LEADER_IN_SECONDS:
if (trackedLeg != null) {
result = trackedLeg.getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE, cache);
@@ -6,7 +6,7 @@ import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor;
import com.sap.sse.datamining.impl.components.ProcessorInstruction;
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
public abstract class AbstractEnrichingProcessor<InputType, ResultType> extends
AbstractSimpleParallelProcessor<InputType, ResultType> {
@@ -17,8 +17,8 @@ public abstract class AbstractEnrichingProcessor<InputType, ResultType> extends
}
@Override
protected ProcessorInstruction<ResultType> createInstruction(final InputType element) {
return new ProcessorInstruction<ResultType>(this) {
protected AbstractProcessorInstruction<ResultType> createInstruction(final InputType element) {
return new AbstractProcessorInstruction<ResultType>(this) {
@Override
public ResultType computeResult() {
@@ -2,6 +2,7 @@ package com.sap.sailing.polars.mining;
import java.util.Collection;
import java.util.Iterator;
import java.util.NavigableSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -88,20 +89,23 @@ public class PolarFixFilterCriteria implements FilterCriterion<GPSFixMovingWithP
private static boolean isInLeadingCompetitorsForLiveRace(TrackedRace trackedRace, Competitor competitor,
double pctOfLeadingCompetitorsToInclude) {
boolean result = false;
Waypoint wayPoint = trackedRace.getMarkPassings(competitor).last().getWaypoint();
if (wayPoint != null) {
Iterator<MarkPassing> markPassingsAtCompetitorsLastWayPoint = trackedRace.getMarkPassingsInOrder(
wayPoint).iterator();
for (int i = 0; i < ((int) Math.max(Math.round(pctOfLeadingCompetitorsToInclude
* getNumberOfCompetitors(trackedRace)), 1)); i++) {
if (markPassingsAtCompetitorsLastWayPoint.hasNext()) {
if (markPassingsAtCompetitorsLastWayPoint.next().getCompetitor().equals(competitor)) {
result = true;
NavigableSet<MarkPassing> markPassingsOfCompetitor = trackedRace.getMarkPassings(competitor);
if (!markPassingsOfCompetitor.isEmpty()) {
Waypoint wayPoint = markPassingsOfCompetitor.last().getWaypoint();
if (wayPoint != null) {
Iterator<MarkPassing> markPassingsAtCompetitorsLastWayPoint = trackedRace.getMarkPassingsInOrder(
wayPoint).iterator();
for (int i = 0; i < ((int) Math.max(
Math.round(pctOfLeadingCompetitorsToInclude * getNumberOfCompetitors(trackedRace)), 1)); i++) {
if (markPassingsAtCompetitorsLastWayPoint.hasNext()) {
if (markPassingsAtCompetitorsLastWayPoint.next().getCompetitor().equals(competitor)) {
result = true;
break;
}
} else {
result = false;
break;
}
} else {
result = false;
break;
}
}
}
@@ -35,7 +35,7 @@ public class RegattaJsonSerializer implements JsonSerializer<Regatta> {
result.put(FIELD_NAME, regatta.getName());
result.put(FIELD_START_DATE, regatta.getStartDate() != null ? regatta.getStartDate().asMillis() : null);
result.put(FIELD_END_DATE, regatta.getStartDate() != null ? regatta.getEndDate().asMillis() : null);
result.put(FIELD_END_DATE, regatta.getEndDate() != null ? regatta.getEndDate().asMillis() : null);
result.put(FIELD_SCORINGSYSTEM, regatta.getScoringScheme().getType().name());
result.put(FIELD_BOATCLASS, regatta.getBoatClass() != null ? regatta.getBoatClass().getName(): null);
@@ -668,6 +668,33 @@ public class RegattasResource extends AbstractSailingServerResource {
return response;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races")
public Response getRaces(@PathParam("regattaname") String regattaName) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = Response.status(Status.NOT_FOUND)
.entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN)
.build();
} else {
JSONObject jsonRaceResults = new JSONObject();
jsonRaceResults.put("regatta", regatta.getName());
JSONArray jsonRaces = new JSONArray();
jsonRaceResults.put("races", jsonRaces);
for (RaceDefinition race : regatta.getAllRaces()) {
JSONObject jsonRace = new JSONObject();
jsonRaces.add(jsonRace);
jsonRace.put("name", race.getName());
jsonRace.put("id", race.getId().toString());
}
String json = jsonRaceResults.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
return response;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/competitors/legs")
@@ -771,6 +798,12 @@ public class RegattasResource extends AbstractSailingServerResource {
UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled
.getMeters()));
}
Distance distanceTraveledIncludingGateStart = trackedLegOfCompetitor.getDistanceTraveledConsideringGateStart(timePoint);
if (distanceTraveledIncludingGateStart != null) {
jsonCompetitorInLeg.put("distanceTraveledIncludingGateStart-m",
UnitSerializationUtil.distanceDecimalFormatter
.format(distanceTraveledIncludingGateStart.getMeters()));
}
try {
Integer rank = ranks.get(competitor);
jsonCompetitorInLeg.put("rank", rank);
@@ -881,6 +914,10 @@ public class RegattasResource extends AbstractSailingServerResource {
jsonCompetitorInLeg.put("distanceTraveled-m",
roundDouble(distanceTraveled.getMeters(), 2));
}
Distance distanceTraveledConsideringGateStart = currentLegOfCompetitor.getDistanceTraveledConsideringGateStart(timePoint);
if (distanceTraveledConsideringGateStart != null) {
jsonCompetitorInLeg.put("distanceTraveledConsideringGateStart-m", roundDouble(distanceTraveledConsideringGateStart.getMeters(), 2));
}
Double gapToLeaderInSeconds = currentLegOfCompetitor.getGapToLeaderInSeconds(timePoint,
WindPositionMode.LEG_MIDDLE);
@@ -52,6 +52,10 @@ name to make sure you get to the event you're interested in.
<td><a href="regattaGetDoc.html">/api/v1/regattas/{name}</a></td>
<td>Gets the details of a regatta</td>
</tr>
<tr>
<td><a href="racesGetDoc.html">/api/v1/regattas/{name}/races</a></td>
<td>Gets the list of races in a regatta</td>
</tr>
<tr>
<td><a href="entriesGetDoc.html">/api/v1/regattas/{name}/entries</a></td>
<td>Gets the entries of a regatta</td>
@@ -0,0 +1,46 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>SAP Sailing Analytics Webservices API Version 1.0 Beta</title>
</head>
<body>
<h1>SAP Sailing Analytics Webservices API Version 1.0 Beta</h1>
<h2>URL: /api/v1/regattas/{regattaname}/races</h2>
<b>Description:</b>
<p>The service gets the names and IDs of all races in a regatta</p>
<br/>
<table>
<tr>
<td>Webservice Type:</td>
<td>REST</td>
</tr>
<tr>
<td>Output format:</td>
<td>Json</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>None</td>
</tr>
<tr>
<td>Optional parameters:</td>
<td>None</td>
</tr>
<tr>
<td>Request method:</td>
<td>GET</td>
</tr>
<tr>
<td>Example:</td>
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/505%20Worlds%202014/races">http://www.sapsailing.com/sailingserver/api/v1/regattas/505%20Worlds%202014/races</a></td>
</tr>
</table>
<div style="height: 1em;"></div>
<a href="index.html">Back to Web Service Overview</a>
</body>
</html>
@@ -210,91 +210,73 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
@Override
public void waitForNextUpdate(int sinceUpdate) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getEndOfTracking() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfNewestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public TimePoint getTimePointOfOldestEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public void lockForRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public void unlockAfterRead(Iterable<MarkPassing> markPassings) {
// TODO Auto-generated method stub
}
@Override
public TimePoint getTimePointOfLastEvent() {
// TODO Auto-generated method stub
return null;
}
@Override
public long getMillisecondsOverWhichToAverageSpeed() {
// TODO Auto-generated method stub
return 0;
}
@Override
public long getMillisecondsOverWhichToAverageWind() {
// TODO Auto-generated method stub
return 0;
}
@Override
public long getDelayToLiveInMillis() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Wind getEstimatedWindDirection(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@@ -305,328 +287,279 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
@Override
public Wind getDirectionFromStartToNextMark(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean raceIsKnownToStartUpwind() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasWindData() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasGPSData() {
// TODO Auto-generated method stub
return false;
}
@Override
public void addListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded,
boolean notifyAboutGPSFixesAlreadyLoaded) {
// TODO Auto-generated method stub
}
@Override
public void removeListener(RaceChangeListener listener) {
// TODO Auto-generated method stub
}
@Override
public Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public Set<WindSource> getWindSourcesToExclude() {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Set<WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
return null;
}
@Override
public WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
// TODO Auto-generated method stub
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public WindStore getWindStore() {
// TODO Auto-generated method stub
return null;
}
@Override
public Competitor getOverallLeader(TimePoint timePoint) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
boolean waitForLatestAnalyses) throws NoWindException {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitUntilLoadingFromWindStoreComplete() throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public TrackedRaceStatus getStatus() {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitUntilNotLoading() {
// TODO Auto-generated method stub
}
@Override
public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
}
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public void attachRaceLog(RaceLog raceLog) {
// TODO Auto-generated method stub
}
@Override
public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
return null;
}
@Override
public void addCourseDesignChangedListener(CourseDesignChangedListener listener) {
// TODO Auto-generated method stub
}
@Override
public Distance getDistanceToStartLine(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceFromStarboardSideOfStartLineWhenPassingStart(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isLive(TimePoint at) {
// TODO Auto-generated method stub
return false;
}
@Override
public Iterable<Sideline> getCourseSidelines() {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceToStartLine(Competitor competitor, long millisecondsBeforeRaceStart) {
// TODO Auto-generated method stub
return null;
}
@Override
public Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart) {
// TODO Auto-generated method stub
return null;
}
public void addStartTimeChangedListener(StartTimeChangedListener listener) {
// TODO Auto-generated method stub
}
@Override
public TimePoint getStartTimeReceived() {
// TODO Auto-generated method stub
return null;
}
@Override
public LineDetails getStartLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public LineDetails getFinishLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getCourseLength() {
// TODO Auto-generated method stub
return null;
}
@Override
public Speed getSpeedWhenCrossingStartLine(Competitor competitor) {
// TODO Auto-generated method stub
return null;
}
@Override
public Distance getDistanceFromStarboardSideOfStartLine(Competitor competitor, TimePoint timePoint) {
// TODO Auto-generated method stub
return null;
}
@Override
public GPSFixStore getGPSFixStore() {
// TODO Auto-generated method stub
return null;
}
@Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RaceLog forRaceLog) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public void addRaceAbortedListener(RaceAbortedListener listener) {
// TODO Auto-generated method stub
}
@Override
public Position getCenterOfCourse(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public void attachRegattaLog(RegattaLog regattaLog) {
// TODO Auto-generated method stub
}
@Override
public void waitForLoadingFromGPSFixStoreToFinishRunning(RegattaLog fromRegattaLog) throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public Boolean isGateStart() {
return null;
}
@Override
public Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
@@ -3,6 +3,19 @@ package com.sap.sse.datamining.shared.impl;
import com.sap.sse.datamining.shared.AdditionalResultData;
import com.sap.sse.datamining.shared.Unit;
/**
* Null Object pattern for {@link AdditionalResultData} with the values:
* <ul>
* <li><b>Retrieved Data Amount</b>: <code>0</code></li>
* <li><b>Calculation Time</b>: <code>0</code></li>
* <li><b>Result Signifier</b>: <code>""</code></li>
* <li><b>Unit</b>: {@link Unit#None}</li>
* <li><b>Unit Signifier</b>: <code>""</code></li>
* <li><b>Value Decimals</b>: <code>0</code></li>
* </ul>
*
* @author Lennart Hensler (D054527)
*/
public class NullAdditionalResultData implements AdditionalResultData {
private static final long serialVersionUID = -8129840449690994767L;
@@ -46,20 +46,15 @@ public class TestDataRetrieverChainCreation {
@Before
public void initializeRetrieverChain() {
dataRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>((Class<Collection<Test_Regatta>>)(Class<?>) Collection.class, Test_HasLegOfCompetitorContext.class, "TestRetrieverChain");
Class<Processor<Collection<Test_Regatta>, Test_Regatta>> regattaRetrieverClass = (Class<Processor<Collection<Test_Regatta>, Test_Regatta>>)(Class<?>) TestRegattaRetrievalProcessor.class;
dataRetrieverChainDefinition.startWith(regattaRetrieverClass, Test_Regatta.class, "regatta");
dataRetrieverChainDefinition.startWith(TestRegattaRetrievalProcessor.class, Test_Regatta.class, "regatta");
Class<Processor<Test_Regatta, Test_HasRaceContext>> raceRetrieverClass =
(Class<Processor<Test_Regatta, Test_HasRaceContext>>)(Class<?>) TestRaceWithContextRetrievalProcessor.class;
dataRetrieverChainDefinition.addAfter(regattaRetrieverClass,
raceRetrieverClass,
Test_HasRaceContext.class, "race");
dataRetrieverChainDefinition.addAfter(TestRegattaRetrievalProcessor.class,
TestRaceWithContextRetrievalProcessor.class,
Test_HasRaceContext.class, "race");
Class<Processor<Test_HasRaceContext, Test_HasLegOfCompetitorContext>> legRetrieverClass =
(Class<Processor<Test_HasRaceContext, Test_HasLegOfCompetitorContext>>)(Class<?>) TestLegOfCompetitorWithContextRetrievalProcessor.class;
dataRetrieverChainDefinition.endWith(raceRetrieverClass,
legRetrieverClass,
Test_HasLegOfCompetitorContext.class, "legOfCompetitor");
dataRetrieverChainDefinition.endWith(TestRaceWithContextRetrievalProcessor.class,
TestLegOfCompetitorWithContextRetrievalProcessor.class,
Test_HasLegOfCompetitorContext.class, "legOfCompetitor");
}
@Before
@@ -179,8 +174,8 @@ public class TestDataRetrieverChainCreation {
dataRetrieverChainDefinition.startWith(regattaRetrieverClass, Test_Regatta.class, "regatta");
dataRetrieverChainDefinition.addAfter(regattaRetrieverClass,
(Class<Processor<Test_Regatta, Test_HasRaceContext>>)(Class<?>) Processor.class,
Test_HasRaceContext.class, "race");
(Class<Processor<Test_Regatta, Test_HasRaceContext>>)(Class<?>) Processor.class,
Test_HasRaceContext.class, "race");
}
@SuppressWarnings("unchecked")
@@ -12,25 +12,28 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Test;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.datamining.AdditionalQueryData;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.QueryState;
import com.sap.sse.datamining.components.FilterCriterion;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.factories.ProcessorFactory;
import com.sap.sse.datamining.functions.Function;
import com.sap.sse.datamining.functions.ParameterProvider;
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor;
import com.sap.sse.datamining.impl.components.AbstractSimpleRetrievalProcessor;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
import com.sap.sse.datamining.impl.components.ParallelFilteringProcessor;
import com.sap.sse.datamining.impl.components.ProcessorInstruction;
import com.sap.sse.datamining.impl.criterias.AbstractFilterCriterion;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.QueryResult;
@@ -63,6 +66,7 @@ public class TestProcessorQuery {
public void testStandardWorkflow() throws InterruptedException, ExecutionException {
Collection<Number> dataSource = createDataSource();
Query<Double> queryWithStandardWorkflow = createQueryWithStandardWorkflow(dataSource);
assertThat(queryWithStandardWorkflow.getState(), is(QueryState.NOT_STARTED));
QueryResult<Double> expectedResult = buildExpectedResult(dataSource);
verifyResult(queryWithStandardWorkflow.run(), expectedResult);
}
@@ -106,8 +110,8 @@ public class TestProcessorQuery {
* their length, extracts the cross sum and aggregates these as sum.
*/
private Query<Double> createQueryWithStandardWorkflow(Collection<Number> dataSource) {
final ThreadPoolExecutor executor = ConcurrencyTestsUtil.getExecutor();
ProcessorQuery<Double, Iterable<Number>> query = new ProcessorQuery<Double, Iterable<Number>>(dataSource, stringMessages, Locale.ENGLISH) {
final ExecutorService executor = ConcurrencyTestsUtil.getExecutor();
ProcessorQuery<Double, Iterable<Number>> query = new ProcessorQuery<Double, Iterable<Number>>(dataSource, stringMessages, Locale.ENGLISH, AdditionalQueryData.NULL_INSTANCE) {
@Override
protected Processor<Iterable<Number>, ?> createFirstProcessor() {
Processor<GroupedDataEntry<Double>, Map<GroupKey, Double>> sumAggregator = processorFactory.createAggregationProcessor(this, AggregatorType.Sum, Double.class);
@@ -135,7 +139,7 @@ public class TestProcessorQuery {
@SuppressWarnings("unchecked")
Processor<Iterable<Number>, Number> retrievalProcessor = new AbstractSimpleRetrievalProcessor<Iterable<Number>, Number>((Class<Iterable<Number>>)(Class<?>) Iterable.class, Number.class,
ConcurrencyTestsUtil.getExecutor(), retrievalResultReceivers) {
ConcurrencyTestsUtil.getExecutor(), retrievalResultReceivers, 0) {
@Override
protected Iterable<Number> retrieveData(Iterable<Number> element) {
return element;
@@ -175,7 +179,7 @@ public class TestProcessorQuery {
Collection<Number> dataSource = new ArrayList<>();
dataSource.add(new Number(10));
ProcessorQuery<Double, Iterable<Number>> query = new ProcessorQuery<Double, Iterable<Number>>(
dataSource, stringMessages, Locale.ENGLISH) {
dataSource, stringMessages, Locale.ENGLISH, AdditionalQueryData.NULL_INSTANCE) {
@SuppressWarnings("unchecked")
@Override
protected Processor<Iterable<Number>, ?> createFirstProcessor() {
@@ -197,6 +201,7 @@ public class TestProcessorQuery {
} catch (TimeoutException e) {
// A timeout exception is expected
}
assertThat(query.getState(), is(QueryState.TIMED_OUT));
ConcurrencyTestsUtil.sleepFor(1000); // Wait if a result is received
assertThat("The processing should be aborted, but received elements", receivedElementOrFinished, is(false));
@@ -211,7 +216,7 @@ public class TestProcessorQuery {
Collection<Number> dataSource = new ArrayList<>();
dataSource.add(new Number(10));
ProcessorQuery<Double, Iterable<Number>> query = new ProcessorQuery<Double, Iterable<Number>>(
dataSource, stringMessages, Locale.ENGLISH) {
dataSource, stringMessages, Locale.ENGLISH, AdditionalQueryData.NULL_INSTANCE) {
@SuppressWarnings("unchecked")
@Override
protected Processor<Iterable<Number>, ?> createFirstProcessor() {
@@ -234,7 +239,9 @@ public class TestProcessorQuery {
}
});
queryRunner.start();
ConcurrencyTestsUtil.sleepFor(500);
ConcurrencyTestsUtil.sleepFor(250);
assertThat(query.getState(), is(QueryState.RUNNING));
ConcurrencyTestsUtil.sleepFor(250);
query.abort();
ConcurrencyTestsUtil.sleepFor(1000); // Wait if a result is received
@@ -275,7 +282,7 @@ public class TestProcessorQuery {
public void testQueryWithTimeoutAndNonBlockingProcess() throws TimeoutException {
final String keyValue = "Sum";
ProcessorQuery<Double, Iterable<Number>> query = new ProcessorQuery<Double, Iterable<Number>>(
createDataSource(), stringMessages, Locale.ENGLISH) {
createDataSource(), stringMessages, Locale.ENGLISH, AdditionalQueryData.NULL_INSTANCE) {
@SuppressWarnings("unchecked")
@Override
protected Processor<Iterable<Number>, ?> createFirstProcessor() {
@@ -286,8 +293,8 @@ public class TestProcessorQuery {
ConcurrencyTestsUtil.getExecutor(),
resultReceivers) {
@Override
protected ProcessorInstruction<Map<GroupKey, Double>> createInstruction(final Iterable<Number> element) {
return new ProcessorInstruction<Map<GroupKey,Double>>(this) {
protected AbstractProcessorInstruction<Map<GroupKey, Double>> createInstruction(final Iterable<Number> element) {
return new AbstractProcessorInstruction<Map<GroupKey,Double>>(this) {
@Override
public Map<GroupKey, Double> computeResult() {
Map<GroupKey, Double> result = new HashMap<>();
@@ -312,5 +319,75 @@ public class TestProcessorQuery {
expectedResult.put(new GenericGroupKey<String>(keyValue), 10358.0);
assertThat(query.run(500, TimeUnit.MILLISECONDS).getResults(), is(expectedResult));
}
@Test
public void testQueryWithSevereFailure() {
Query<Double> query = new ProcessorQuery<Double, Double>(0.0) {
@SuppressWarnings("unchecked")
@Override
protected Processor<Double, ?> createFirstProcessor() {
Collection<Processor<Map<GroupKey, Double>, ?>> resultReceivers = new ArrayList<>();
resultReceivers.add(this.getResultReceiver());
return new AbstractSimpleParallelProcessor<Double, Map<GroupKey, Double>>(Double.class,
(Class<Map<GroupKey, Double>>)(Class<?>) Map.class,
ConcurrencyTestsUtil.getExecutor(),
resultReceivers) {
@Override
protected AbstractProcessorInstruction<Map<GroupKey, Double>> createInstruction(Double element) {
return new AbstractProcessorInstruction<Map<GroupKey,Double>>(this) {
@Override
protected Map<GroupKey, Double> computeResult() throws Exception {
throw new RejectedExecutionException("This should cause an error during the query processing");
}
};
}
@Override
protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) {
}
};
}
};
try {
query.run();
fail("The previous line should throw a runtime exception");
} catch (RuntimeException e) {
// A RuntimeException wiht a RejectedExecitonException is expected
if (!(e.getCause() instanceof RejectedExecutionException)) {
throw e;
}
}
assertThat(query.getState(), is(QueryState.ERROR));
}
@Test
public void testQueryWithFailure() {
Query<Double> query = new ProcessorQuery<Double, Double>(0.0) {
@SuppressWarnings("unchecked")
@Override
protected Processor<Double, ?> createFirstProcessor() {
Collection<Processor<Map<GroupKey, Double>, ?>> resultReceivers = new ArrayList<>();
resultReceivers.add(this.getResultReceiver());
return new AbstractSimpleParallelProcessor<Double, Map<GroupKey, Double>>(Double.class,
(Class<Map<GroupKey, Double>>)(Class<?>) Map.class,
ConcurrencyTestsUtil.getExecutor(),
resultReceivers) {
@Override
protected AbstractProcessorInstruction<Map<GroupKey, Double>> createInstruction(Double element) {
return new AbstractProcessorInstruction<Map<GroupKey,Double>>(this) {
@Override
protected Map<GroupKey, Double> computeResult() throws Exception {
throw new NullPointerException("This should cause a failure during the query processing");
}
};
}
@Override
protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) {
}
};
}
};
query.run();
assertThat(query.getState(), is(QueryState.FAILURE));
}
}
@@ -69,8 +69,8 @@ public class TestAbstractParallelProcessorFinishing {
private AbstractSimpleParallelProcessor<Integer, Integer> createProcessor(Collection<Processor<Integer, ?>> receivers) {
return new AbstractSimpleParallelProcessor<Integer, Integer>(Integer.class, Integer.class, ConcurrencyTestsUtil.getExecutor(), receivers) {
@Override
protected ProcessorInstruction<Integer> createInstruction(Integer partialElement) {
return new ProcessorInstruction<Integer>(this) {
protected AbstractProcessorInstruction<Integer> createInstruction(Integer partialElement) {
return new AbstractProcessorInstruction<Integer>(this) {
@Override
public Integer computeResult() {
while (instructionIsWorking) {
@@ -38,8 +38,8 @@ public class TestAbstractParallelProcessorWithManySimpleInstructions {
receivers.add(receiver);
processor = new AbstractSimpleParallelProcessor<Integer, Integer>(Integer.class, Integer.class, ConcurrencyTestsUtil.getExecutor(), receivers) {
@Override
protected ProcessorInstruction<Integer> createInstruction(final Integer element) {
return new ProcessorInstruction<Integer>(this) {
protected AbstractProcessorInstruction<Integer> createInstruction(final Integer element) {
return new AbstractProcessorInstruction<Integer>(this) {
@Override
public Integer computeResult() {
return element;
@@ -27,8 +27,8 @@ public class TestPartitioningOfAbstractSimpleParallelProcessor {
}
@Override
protected ProcessorInstruction<Integer> createInstruction(Integer partialElement) {
return new ProcessorInstruction<Integer>(this) {
protected AbstractProcessorInstruction<Integer> createInstruction(Integer partialElement) {
return new AbstractProcessorInstruction<Integer>(this) {
@Override
public Integer computeResult() {
return 0;
@@ -0,0 +1,34 @@
package com.sap.sse.datamining.impl.components;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.PriorityQueue;
import java.util.Queue;
import org.junit.Test;
import com.sap.sse.datamining.test.util.components.NullProcessorInstruction;
public class TestProcessorInstructionOrdering {
@Test
public void testOrderInPriorityQueue() {
Queue<NullProcessorInstruction<?>> priorityQueue = new PriorityQueue<>();
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.createRetrievalPriority(0)));
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.createRetrievalPriority(1)));
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.Grouping));
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.Extraction));
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.Filtration));
priorityQueue.add(new NullProcessorInstruction<>(null, ProcessorInstructionPriority.Aggregation));
assertThat(priorityQueue.poll().getPriority(), is(0));
assertThat(priorityQueue.poll().getPriority(), is(1));
assertThat(priorityQueue.poll().getPriority(), is(1));
assertThat(priorityQueue.poll().getPriority(), is(2));
assertThat(priorityQueue.poll().getPriority(), is(Integer.MAX_VALUE - 1));
assertThat(priorityQueue.poll().getPriority(), is(Integer.MAX_VALUE));
}
}
@@ -72,7 +72,7 @@ public class TestRetrieverFilterProcessorChain {
resultReceivers.add(retrievalProcessor);
@SuppressWarnings("unchecked")
Processor<Iterable<Iterable<Integer>>, Iterable<Integer>> layeredRetrievalProcessor = new AbstractSimpleRetrievalProcessor<Iterable<Iterable<Integer>>, Iterable<Integer>>((Class<Iterable<Iterable<Integer>>>)(Class<?>) Iterable.class, (Class<Iterable<Integer>>)(Class<?>) Iterable.class,
ConcurrencyTestsUtil.getExecutor(), resultReceivers) {
ConcurrencyTestsUtil.getExecutor(), resultReceivers, 0) {
@Override
protected Iterable<Iterable<Integer>> retrieveData(Iterable<Iterable<Integer>> element) {
return element;
@@ -114,7 +114,7 @@ public class TestRetrieverFilterProcessorChain {
Collection<Processor<Integer, ?>> retrievalResultReceivers = new ArrayList<>();
retrievalResultReceivers.add(filtrationProcessor);
retrievalProcessor = new AbstractSimpleRetrievalProcessor<Iterable<Integer>, Integer>((Class<Iterable<Integer>>)(Class<?>) Iterable.class, Integer.class, ConcurrencyTestsUtil.getExecutor(), retrievalResultReceivers) {
retrievalProcessor = new AbstractSimpleRetrievalProcessor<Iterable<Integer>, Integer>((Class<Iterable<Integer>>)(Class<?>) Iterable.class, Integer.class, ConcurrencyTestsUtil.getExecutor(), retrievalResultReceivers, 1) {
@Override
protected Iterable<Integer> retrieveData(Iterable<Integer> element) {
return element;
@@ -8,19 +8,18 @@ import static org.junit.Assert.fail;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.DataMiningExecutorService;
import com.sap.sse.datamining.shared.GroupKey;
public class ConcurrencyTestsUtil extends TestsUtil {
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
private static final ExecutorService executor = new DataMiningExecutorService(THREAD_POOL_SIZE);
public static ThreadPoolExecutor getExecutor() {
public static ExecutorService getExecutor() {
return executor;
}
@@ -1,31 +0,0 @@
package com.sap.sse.datamining.test.util.components;
import com.sap.sse.datamining.test.util.ConcurrencyTestsUtil;
public class BlockingInstructionWrapper implements Runnable {
private Runnable innerInstruction;
private boolean workIsInProgress;
public BlockingInstructionWrapper(Runnable innerInstruction) {
this.innerInstruction = innerInstruction;
workIsInProgress = true;
}
@Override
public void run() {
while (workIsInProgress) {
ConcurrencyTestsUtil.sleepFor(100);
}
innerInstruction.run();
}
/**
* Tells the instruction, that the work is now done. It won't block it's inner instruction anymore and calls the run method of it.
*/
public void workIsNowFinished() {
workIsInProgress = false;
}
}
@@ -6,7 +6,7 @@ import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor;
import com.sap.sse.datamining.impl.components.ProcessorInstruction;
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
public abstract class BlockingProcessor<InputType, ResultType> extends AbstractSimpleParallelProcessor<InputType, ResultType> {
private final long timeToBlockInMillis;
@@ -19,8 +19,8 @@ public abstract class BlockingProcessor<InputType, ResultType> extends AbstractS
}
@Override
protected ProcessorInstruction<ResultType> createInstruction(InputType element) {
return new ProcessorInstruction<ResultType>(this) {
protected AbstractProcessorInstruction<ResultType> createInstruction(InputType element) {
return new AbstractProcessorInstruction<ResultType>(this) {
@Override
public ResultType computeResult() throws Exception {
Thread.sleep(timeToBlockInMillis);
@@ -0,0 +1,23 @@
package com.sap.sse.datamining.test.util.components;
import com.sap.sse.datamining.impl.components.AbstractPartitioningParallelProcessor;
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
import com.sap.sse.datamining.impl.components.ProcessorInstructionPriority;
public class NullProcessorInstruction<ResultType> extends AbstractProcessorInstruction<ResultType> {
public NullProcessorInstruction(AbstractPartitioningParallelProcessor<?, ?, ResultType> processor,
ProcessorInstructionPriority priority) {
super(processor, priority);
}
@Override
protected ResultType computeResult() throws Exception {
return null;
}
public int getPriority() {
return super.getPriority();
}
}
@@ -15,8 +15,8 @@ import com.sap.sse.datamining.test.functions.registry.test_contexts.Test_HasRace
public class TestLegOfCompetitorWithContextRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<Test_HasRaceContext, Test_HasLegOfCompetitorContext>{
public TestLegOfCompetitorWithContextRetrievalProcessor(ExecutorService executor, Collection<Processor<Test_HasLegOfCompetitorContext, ?>> resultReceivers) {
super(Test_HasRaceContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers);
public TestLegOfCompetitorWithContextRetrievalProcessor(ExecutorService executor, Collection<Processor<Test_HasLegOfCompetitorContext, ?>> resultReceivers, int retrievalLevel) {
super(Test_HasRaceContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -15,8 +15,8 @@ public class TestRaceWithContextRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<Test_Regatta, Test_HasRaceContext> {
public TestRaceWithContextRetrievalProcessor(ExecutorService executor,
Collection<Processor<Test_HasRaceContext, ?>> resultReceivers) {
super(Test_Regatta.class, Test_HasRaceContext.class, executor, resultReceivers);
Collection<Processor<Test_HasRaceContext, ?>> resultReceivers, int retrievalLevel) {
super(Test_Regatta.class, Test_HasRaceContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -11,8 +11,8 @@ public class TestRegattaRetrievalProcessor extends
AbstractSimpleRetrievalProcessor<Collection<Test_Regatta>, Test_Regatta> {
@SuppressWarnings("unchecked")
public TestRegattaRetrievalProcessor(ExecutorService executor, Collection<Processor<Test_Regatta, ?>> resultReceivers) {
super((Class<Collection<Test_Regatta>>)(Class<?>) Collection.class, Test_Regatta.class, executor, resultReceivers);
public TestRegattaRetrievalProcessor(ExecutorService executor, Collection<Processor<Test_Regatta, ?>> resultReceivers, int retrievalLevel) {
super((Class<Collection<Test_Regatta>>)(Class<?>) Collection.class, Test_Regatta.class, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -0,0 +1,16 @@
package com.sap.sse.datamining;
import java.util.UUID;
import com.sap.sse.datamining.Query.QueryType;
import com.sap.sse.datamining.impl.NullAdditionalQueryData;
public interface AdditionalQueryData {
public AdditionalQueryData NULL_INSTANCE = new NullAdditionalQueryData();
public QueryType getType();
public UUID getDataRetrieverChainID();
}
@@ -7,8 +7,12 @@ import com.sap.sse.datamining.shared.QueryResult;
public interface Query<AggregatedType> {
public enum QueryType { STATISTIC, DIMENSION_VALUES, OTHER }
public QueryState getState();
public AdditionalQueryData getAdditionalData();
public QueryResult<AggregatedType> run();
public QueryResult<AggregatedType> run(long timeout, TimeUnit unit) throws TimeoutException;
@@ -15,11 +15,13 @@ import com.sap.sse.datamining.DataRetrieverChainBuilder;
import com.sap.sse.datamining.DataRetrieverChainDefinition;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.QueryDefinition;
import com.sap.sse.datamining.Query.QueryType;
import com.sap.sse.datamining.components.FilterCriterion;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.functions.Function;
import com.sap.sse.datamining.functions.ParameterProvider;
import com.sap.sse.datamining.impl.ProcessorQuery;
import com.sap.sse.datamining.impl.SimpleAdditionalQueryData;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
import com.sap.sse.datamining.impl.criterias.AndCompoundFilterCriterion;
import com.sap.sse.datamining.impl.criterias.CompoundFilterCriterion;
@@ -32,7 +34,7 @@ public class QueryFactory {
public <DataSourceType, DataType, ResultType> Query<ResultType> createQuery(DataSourceType dataSource, QueryDefinition<DataSourceType, DataType, ResultType> queryDefinition,
ResourceBundleStringMessages stringMessages, ExecutorService executor) {
return new ProcessorQuery<ResultType, DataSourceType>(dataSource, stringMessages, queryDefinition.getLocale()) {
return new ProcessorQuery<ResultType, DataSourceType>(dataSource, stringMessages, queryDefinition.getLocale(), new SimpleAdditionalQueryData(QueryType.STATISTIC, queryDefinition.getDataRetrieverChainDefinition().getID())) {
@Override
protected Processor<DataSourceType, ?> createFirstProcessor() {
ProcessorFactory processorFactory = new ProcessorFactory(executor);
@@ -118,7 +120,7 @@ public class QueryFactory {
DataRetrieverChainDefinition<DataSource, ?> dataRetrieverChainDefinition, int retrieverLevel,
Iterable<Function<?>> dimensions, Locale locale,
ResourceBundleStringMessages stringMessages, ExecutorService executor) {
return new ProcessorQuery<Set<Object>, DataSource>(dataSource, stringMessages, locale) {
return new ProcessorQuery<Set<Object>, DataSource>(dataSource, stringMessages, locale, new SimpleAdditionalQueryData(QueryType.DIMENSION_VALUES, dataRetrieverChainDefinition.getID())) {
@Override
protected Processor<DataSource, ?> createFirstProcessor() {
ProcessorFactory processorFactory = new ProcessorFactory(executor);
@@ -0,0 +1,13 @@
package com.sap.sse.datamining.impl;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DataMiningExecutorService extends ThreadPoolExecutor {
public DataMiningExecutorService(int corePoolSize) {
super(corePoolSize, corePoolSize, 60, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());
}
}
@@ -3,10 +3,6 @@ package com.sap.sse.datamining.impl;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
@@ -27,8 +23,8 @@ import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
public class DataMiningFrameworkActivator implements BundleActivator {
private static final Logger logger = Logger.getLogger(DataMiningFrameworkActivator.class.getName());
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static final String STRING_MESSAGES_BASE_NAME = "stringmessages/StringMessages";
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static DataMiningFrameworkActivator INSTANCE;
@@ -38,13 +34,7 @@ public class DataMiningFrameworkActivator implements BundleActivator {
private final ModifiableDataMiningServer dataMiningServer;
public DataMiningFrameworkActivator() {
ExecutorService executor = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
r.run();
}
});
ExecutorService executor = new DataMiningExecutorService(THREAD_POOL_SIZE);
FunctionManager functionManager = new FunctionManager();
DataRetrieverChainDefinitionRegistry dataRetrieverChainDefinitionRegistry = new SimpleDataRetrieverChainDefinitionRegistry();
@@ -47,7 +47,7 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer {
this.stringMessages = new CompoundResourceBundleStringMessages();
this.executorService = executorService;
this.queryFactory = new QueryFactory();
dataMiningQueryManager = new ConcurrentDataMiningQueryManager();
dataMiningQueryManager = new StrategyPerQueryTypeManager();
this.functionRegistry = functionRegistry;
this.functionProvider = functionProvider;
dataSourceProviderMappedByDataSourceType = new HashMap<>();
@@ -0,0 +1,81 @@
package com.sap.sse.datamining.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.sap.sse.datamining.DataMiningQueryManager;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.shared.DataMiningSession;
import com.sap.sse.datamining.shared.QueryResult;
public class ManyQueriesPerRetrieverChainManager implements DataMiningQueryManager {
private final ConcurrentMap<DataMiningSession, UUID> currentRetrieverChainMappedBySession;
private final Map<DataMiningSession, Collection<Query<?>>> queriesMappedBySession;
public ManyQueriesPerRetrieverChainManager() {
currentRetrieverChainMappedBySession = new ConcurrentHashMap<>();
queriesMappedBySession = new HashMap<>();
}
@Override
public <ResultType> QueryResult<ResultType> runNewAndAbortPrevious(DataMiningSession session, Query<ResultType> query) {
if (session == null || query == null) {
throw new NullPointerException();
}
abortPreviousQueries(session, query);
updateCurrentRetrieverChain(session, query);
registerNewQuery(session, query);
QueryResult<ResultType> result = query.run();
unregisterQuery(session, query);
return result;
}
private void abortPreviousQueries(DataMiningSession session, Query<?> newQuery) {
final UUID newRetrieverChainID = newQuery.getAdditionalData().getDataRetrieverChainID();
if (currentRetrieverChainMappedBySession.containsKey(session) &&
!currentRetrieverChainMappedBySession.get(session).equals(newRetrieverChainID)) {
Collection<Query<?>> previousQueries = queriesMappedBySession.get(session);
for (Query<?> previousQuery : previousQueries) {
previousQuery.abort();
}
queriesMappedBySession.remove(session, previousQueries);
currentRetrieverChainMappedBySession.remove(session);
currentRetrieverChainMappedBySession.putIfAbsent(session, newRetrieverChainID);
}
}
private void updateCurrentRetrieverChain(DataMiningSession session, Query<?> query) {
UUID retrieverChainID = query.getAdditionalData().getDataRetrieverChainID();
UUID previousRetrieverChainID = currentRetrieverChainMappedBySession.putIfAbsent(session, retrieverChainID);
if (previousRetrieverChainID != null && !previousRetrieverChainID.equals(retrieverChainID)) {
currentRetrieverChainMappedBySession.replace(session, previousRetrieverChainID, retrieverChainID);
}
}
private <ResultType> void registerNewQuery(DataMiningSession session, Query<ResultType> query) {
if (!queriesMappedBySession.containsKey(session)) {
Set<Query<?>> concurrentSet = Collections.newSetFromMap(new ConcurrentHashMap<Query<?>, Boolean>());
queriesMappedBySession.put(session, concurrentSet);
}
queriesMappedBySession.get(session).add(query);
}
private void unregisterQuery(DataMiningSession session, Query<?> finishedQuery) {
UUID retrieverChainID = finishedQuery.getAdditionalData().getDataRetrieverChainID();
if (currentRetrieverChainMappedBySession.containsKey(session) &&
currentRetrieverChainMappedBySession.get(session).equals(retrieverChainID)) {
queriesMappedBySession.get(session).remove(finishedQuery);
}
}
}
@@ -0,0 +1,31 @@
package com.sap.sse.datamining.impl;
import java.util.UUID;
import com.sap.sse.datamining.AdditionalQueryData;
import com.sap.sse.datamining.Query.QueryType;
/**
* Null Object pattern for {@link AdditionalQueryData} with the values:
* <ul>
* <li><b>Type</b>: {@link QueryType#OTHER}</li>
* <li><b>Data Retriever Chain ID</b>: {@link UUID#UUID(long, long) UUID(0, 0)}</li>
* </ul>
*
* @author Lennart Hensler (D054527)
*/
public class NullAdditionalQueryData implements AdditionalQueryData {
private final UUID nullUUID = new UUID(0, 0);
@Override
public QueryType getType() {
return QueryType.OTHER;
}
@Override
public UUID getDataRetrieverChainID() {
return nullUUID;
}
}
@@ -14,14 +14,17 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sse.datamining.AdditionalQueryData;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.QueryState;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.OverwritingResultDataBuilder;
import com.sap.sse.datamining.shared.AdditionalResultData;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.QueryResult;
import com.sap.sse.datamining.shared.QueryResultState;
import com.sap.sse.datamining.shared.impl.NullAdditionalResultData;
import com.sap.sse.datamining.shared.impl.QueryResultImpl;
import com.sap.sse.i18n.ResourceBundleStringMessages;
@@ -36,26 +39,33 @@ public abstract class ProcessorQuery<AggregatedType, DataSourceType> implements
private final ResourceBundleStringMessages stringMessages;
private final Locale locale;
private final AdditionalQueryData additionalData;
private final Object monitorObject = new Object();
private Thread workingThread;
private QueryState state;
/**
* Creates a query that returns a result without any additional data (like the calculation time or the retrieved data amount).<br>
* Creates a query
* <ul>
* <li> with no {@link AdditionalQueryData} (more exactly with {@link NullAdditionalQueryData} as additional data).</li>
* <li> that returns a result without {@link AdditionalResultData} (more exactly with {@link NullAdditionalResultData} as additional data).</li>
* </ul>
*
* This is useful for non user specific queries, like retrieving the dimension values.
*/
public ProcessorQuery(DataSourceType dataSource) {
this(dataSource, null, null);
this(dataSource, null, null, AdditionalQueryData.NULL_INSTANCE);
}
/**
* Creates a query that returns a result with additional data.
*/
public ProcessorQuery(DataSourceType dataSource, ResourceBundleStringMessages stringMessages, Locale locale) {
public ProcessorQuery(DataSourceType dataSource, ResourceBundleStringMessages stringMessages, Locale locale, AdditionalQueryData additionalData) {
this.dataSource = dataSource;
this.stringMessages = stringMessages;
this.locale = locale;
this.additionalData = additionalData;
state = QueryState.NOT_STARTED;
resultReceiver = new ProcessResultReceiver();
@@ -68,6 +78,11 @@ public abstract class ProcessorQuery<AggregatedType, DataSourceType> implements
public QueryState getState() {
return state;
}
@Override
public AdditionalQueryData getAdditionalData() {
return additionalData;
}
@Override
public QueryResult<AggregatedType> run() {
@@ -238,7 +253,7 @@ public abstract class ProcessorQuery<AggregatedType, DataSourceType> implements
@Override
public void finish() throws InterruptedException {
synchronized (monitorObject) {
state = QueryState.NORMAL;
state = state == QueryState.RUNNING ? QueryState.NORMAL : state;
monitorObject.notify();
}
}
@@ -0,0 +1,28 @@
package com.sap.sse.datamining.impl;
import java.util.UUID;
import com.sap.sse.datamining.AdditionalQueryData;
import com.sap.sse.datamining.Query.QueryType;
public class SimpleAdditionalQueryData implements AdditionalQueryData {
private final QueryType type;
private final UUID dataRetrieverChainID;
public SimpleAdditionalQueryData(QueryType type, UUID dataRetrieverChainID) {
this.type = type;
this.dataRetrieverChainID = dataRetrieverChainID;
}
@Override
public QueryType getType() {
return type;
}
@Override
public UUID getDataRetrieverChainID() {
return dataRetrieverChainID;
}
}
@@ -146,24 +146,24 @@ public class SimpleDataRetrieverChainBuilder<DataSourceType> implements DataRetr
FilterCriterion<ResultType> filter = (FilterCriterion<ResultType>) filters.get(retrieverTypeIndex);
Class<Processor<?, ResultType>> retrieverType = (Class<Processor<?, ResultType>>)(Class<?>) dataRetrieverTypeWithInformation.getRetrieverType();
return createRetriever(retrieverType, retrievedDataType, resultReceivers, filter);
return createRetriever(retrieverType, retrievedDataType, resultReceivers, filter, retrieverTypeIndex);
}
private <ResultType> Processor<?, ResultType> createRetriever(Class<Processor<?, ResultType>> retrieverType, Class<ResultType> retrievedDataType,
Collection<Processor<ResultType, ?>> resultReceivers, FilterCriterion<ResultType> filter) {
Collection<Processor<ResultType, ?>> resultReceivers, FilterCriterion<ResultType> filter, int retrieverTypeIndex) {
Constructor<Processor<?, ResultType>> retrieverConstructor = null;
try {
retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class);
retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalArgumentException("Couldn't get an usable constructor from the given retrieverType '"
+ retrieverType.getSimpleName() + "'", e);
}
return constructRetriever(retrieverConstructor, retrievedDataType, resultReceivers, filter);
return constructRetriever(retrieverConstructor, retrievedDataType, resultReceivers, filter, retrieverTypeIndex);
}
private <ResultType> Processor<?, ResultType> constructRetriever(Constructor<Processor<?, ResultType>> retrieverConstructor, Class<ResultType> retrievedDataType,
Collection<Processor<ResultType, ?>> resultReceivers, FilterCriterion<ResultType> filter) {
Collection<Processor<ResultType, ?>> resultReceivers, FilterCriterion<ResultType> filter, int retrieverTypeIndex) {
try {
Collection<Processor<ResultType, ?>> retrievalResultReceivers = resultReceivers;
if (filter != null) {
@@ -173,7 +173,7 @@ public class SimpleDataRetrieverChainBuilder<DataSourceType> implements DataRetr
retrievalResultReceivers.add(filteringProcessor);
}
return retrieverConstructor.newInstance(executor, retrievalResultReceivers);
return retrieverConstructor.newInstance(executor, retrievalResultReceivers, retrieverTypeIndex);
} catch (InstantiationException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
throw new UnsupportedOperationException("Couldn't create a data retriever instance with the constructor "
@@ -105,7 +105,7 @@ public class SimpleDataRetrieverChainDefinition<DataSourceType, DataType> implem
private <InputType, ResultType> void checkThatRetrieverHasUsableConstructor(
Class<? extends Processor<InputType, ResultType>> retrieverType) {
try {
retrieverType.getConstructor(ExecutorService.class, Collection.class);
retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class);
} catch (NoSuchMethodException | SecurityException e) {
throw new IllegalArgumentException("Couldn't get an usable constructor from the given nextRetrieverType '"
+ retrieverType.getSimpleName() + "'", e);
@@ -8,11 +8,11 @@ import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.shared.DataMiningSession;
import com.sap.sse.datamining.shared.QueryResult;
public class ConcurrentDataMiningQueryManager implements DataMiningQueryManager {
public class SingleQueryPerSessionManager implements DataMiningQueryManager {
private final ConcurrentMap<DataMiningSession, Query<?>> queryMappedBySession;
public ConcurrentDataMiningQueryManager() {
public SingleQueryPerSessionManager() {
queryMappedBySession = new ConcurrentHashMap<>();
}
@@ -23,15 +23,14 @@ public class ConcurrentDataMiningQueryManager implements DataMiningQueryManager
throw new NullPointerException();
}
abortPreviousQueries(session, query);
abortPreviousQueries(session);
registerNewQuery(session, query);
QueryResult<ResultType> result = query.run();
unregisterQuery(session, query);
return result;
}
private void abortPreviousQueries(DataMiningSession session, Query<?> query) {
// TODO handle different types of queries (a statistics query doesn't have to be aborted, if a new dimension values query wants to run)
private void abortPreviousQueries(DataMiningSession session) {
if (queryMappedBySession.containsKey(session)) {
Query<?> previousQuery = queryMappedBySession.get(session);
previousQuery.abort();
@@ -46,8 +45,8 @@ public class ConcurrentDataMiningQueryManager implements DataMiningQueryManager
}
}
private void unregisterQuery(DataMiningSession session, Query<?> query) {
queryMappedBySession.remove(session, query);
private void unregisterQuery(DataMiningSession session, Query<?> finishedQuery) {
queryMappedBySession.remove(session, finishedQuery);
}
}
@@ -0,0 +1,28 @@
package com.sap.sse.datamining.impl;
import java.util.HashMap;
import java.util.Map;
import com.sap.sse.datamining.DataMiningQueryManager;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.Query.QueryType;
import com.sap.sse.datamining.shared.DataMiningSession;
import com.sap.sse.datamining.shared.QueryResult;
public class StrategyPerQueryTypeManager implements DataMiningQueryManager {
private final Map<QueryType, DataMiningQueryManager> managersMappedByQueryType;
public StrategyPerQueryTypeManager() {
managersMappedByQueryType = new HashMap<>();
managersMappedByQueryType.put(QueryType.STATISTIC, new SingleQueryPerSessionManager());
managersMappedByQueryType.put(QueryType.DIMENSION_VALUES, new ManyQueriesPerRetrieverChainManager());
managersMappedByQueryType.put(QueryType.OTHER, new SingleQueryPerSessionManager());
}
@Override
public <ResultType> QueryResult<ResultType> runNewAndAbortPrevious(DataMiningSession session, Query<ResultType> query) {
return managersMappedByQueryType.get(query.getAdditionalData().getType()).runNewAndAbortPrevious(session, query);
}
}
@@ -46,8 +46,8 @@ public abstract class AbstractParallelMultiDimensionalNestingGroupingProcessor<D
}
@Override
protected ProcessorInstruction<GroupedDataEntry<DataType>> createInstruction(final DataType element) {
return new ProcessorInstruction<GroupedDataEntry<DataType>>(this) {
protected AbstractProcessorInstruction<GroupedDataEntry<DataType>> createInstruction(final DataType element) {
return new AbstractProcessorInstruction<GroupedDataEntry<DataType>>(this, ProcessorInstructionPriority.Grouping) {
@Override
public GroupedDataEntry<DataType> computeResult() {
return new GroupedDataEntry<DataType>(createCompoundKeyFor(element,
@@ -36,7 +36,7 @@ public abstract class AbstractPartitioningParallelProcessor<InputType, WorkingTy
public void processElement(InputType element) {
if (!isFinished && !isAborted) {
for (WorkingType partialElement : partitionElement(element)) {
final ProcessorInstruction<ResultType> instruction = createInstruction(partialElement);
final AbstractProcessorInstruction<ResultType> instruction = createInstruction(partialElement);
if (isInstructionValid(instruction)) {
unfinishedInstructionsCounter.getAndIncrement();
try {
@@ -51,7 +51,7 @@ public abstract class AbstractPartitioningParallelProcessor<InputType, WorkingTy
}
}
private boolean isInstructionValid(ProcessorInstruction<ResultType> instruction) {
private boolean isInstructionValid(AbstractProcessorInstruction<ResultType> instruction) {
return instruction != null;
}
@@ -76,7 +76,7 @@ public abstract class AbstractPartitioningParallelProcessor<InputType, WorkingTy
}
}
protected abstract ProcessorInstruction<ResultType> createInstruction(final WorkingType partialElement);
protected abstract AbstractProcessorInstruction<ResultType> createInstruction(final WorkingType partialElement);
protected abstract Iterable<WorkingType> partitionElement(InputType element);
@@ -0,0 +1,76 @@
package com.sap.sse.datamining.impl.components;
public abstract class AbstractProcessorInstruction<ResultType> implements Runnable, Comparable<AbstractProcessorInstruction<?>> {
private final AbstractPartitioningParallelProcessor<?, ?, ResultType> processor;
private final int priority;
/**
* Creates an instruction with a priority of 0, which is the highest priority.<br />
* Use only, if <b>no processor</b> in the chain creates instruction <b>with</b> priorities other than 0 or
* if the chain doesn't use priorities at all.
* If this isn't the case use
* {@link #ProcessorInstruction(AbstractPartitioningParallelProcessor, ProcessorInstructionPriority)}
* instead.
*
* @param processor The processor, that processes this instruction
*/
public AbstractProcessorInstruction(AbstractPartitioningParallelProcessor<?, ?, ResultType> processor) {
this(processor, 0);
}
/**
* Creates an instruction with the given {@linkplain ProcessorInstructionPriority priority}.
*
* @param processor The processor, that processes this instruction
* @param priority The priority of this instruction
*/
public AbstractProcessorInstruction(AbstractPartitioningParallelProcessor<?, ?, ResultType> processor,
ProcessorInstructionPriority priority) {
this(processor, priority.asIntValue());
}
/**
* Creates an instruction with the given priority as <code>int</code>.<br />
* <b>Use only, if you need instructions with custom priorities and if you know what you're doing!</b>
* If this isn't the case use
* {@link #ProcessorInstruction(AbstractPartitioningParallelProcessor, ProcessorInstructionPriority)}
* instead.
*
* @param processor The processor, that processes this instruction
* @param priority The priority of this instruction. <code>0</code> is the highest priority.
*/
public AbstractProcessorInstruction(AbstractPartitioningParallelProcessor<?, ?, ResultType> processor,
int priority) {
this.processor = processor;
this.priority = priority;
}
@Override
public void run() {
try {
ResultType result = computeResult();
if (processor.isResultValid(result) && !processor.isAborted()) {
processor.forwardResultToReceivers(result);
}
} catch (Exception e) {
if (!processor.isAborted() || !(e instanceof InterruptedException)) {
processor.onFailure(e);
}
} finally {
processor.getUnfinishedInstructionsCounter().getAndDecrement();
}
}
protected abstract ResultType computeResult() throws Exception;
protected int getPriority() {
return priority;
}
@Override
public int compareTo(AbstractProcessorInstruction<?> instruction) {
return Integer.compare(priority, instruction.priority);
}
}
@@ -11,17 +11,29 @@ import com.sap.sse.datamining.components.Processor;
public abstract class AbstractRetrievalProcessor<InputType, WorkingType, ResultType> extends
AbstractPartitioningParallelProcessor<InputType, WorkingType, ResultType> {
private final int retrievalLevel;
private final Lock retrievedDataAmountLock;
private int retrievedDataAmount;
public AbstractRetrievalProcessor(Class<InputType> inputType, Class<ResultType> resultType, ExecutorService executor, Collection<Processor<ResultType, ?>> resultReceivers) {
/**
*
* @param inputType
* @param resultType
* @param executor
* @param resultReceivers
* @param retrievalLevel The position of this retriever in it's chain. <code>0</code> represents the first.
*/
public AbstractRetrievalProcessor(Class<InputType> inputType, Class<ResultType> resultType,
ExecutorService executor, Collection<Processor<ResultType, ?>> resultReceivers, int retrievalLevel) {
super(inputType, resultType, executor, resultReceivers);
this.retrievalLevel = retrievalLevel;
retrievedDataAmountLock = new ReentrantLock();
}
@Override
protected ProcessorInstruction<ResultType> createInstruction(final WorkingType partialElement) {
return new ProcessorInstruction<ResultType>(this) {
protected AbstractProcessorInstruction<ResultType> createInstruction(final WorkingType partialElement) {
return new AbstractProcessorInstruction<ResultType>(this, ProcessorInstructionPriority.createRetrievalPriority(retrievalLevel)) {
@Override
public ResultType computeResult() {
incrementRetrievedDataAmount();
@@ -21,6 +21,6 @@ public abstract class AbstractSimpleParallelProcessor<InputType, ResultType>
//Redefinition of the method to set the parameter name to element instead of partial element.
//This makes the implementation of sub classes more fluent.
@Override
protected abstract ProcessorInstruction<ResultType> createInstruction(InputType element);
protected abstract AbstractProcessorInstruction<ResultType> createInstruction(InputType element);
}
@@ -8,8 +8,17 @@ import com.sap.sse.datamining.components.Processor;
public abstract class AbstractSimpleRetrievalProcessor<InputType, ResultType> extends
AbstractRetrievalProcessor<InputType, ResultType, ResultType> {
public AbstractSimpleRetrievalProcessor(Class<InputType> inputType, Class<ResultType> resultType, ExecutorService executor, Collection<Processor<ResultType, ?>> resultReceivers) {
super(inputType, resultType, executor, resultReceivers);
/**
*
* @param inputType
* @param resultType
* @param executor
* @param resultReceivers
* @param retrievalLevel The position of this retriever in it's chain. <code>0</code> represents the first.
*/
public AbstractSimpleRetrievalProcessor(Class<InputType> inputType, Class<ResultType> resultType,
ExecutorService executor, Collection<Processor<ResultType, ?>> resultReceivers, int retrievalLevel) {
super(inputType, resultType, executor, resultReceivers, retrievalLevel);
}
@Override
@@ -23,8 +23,8 @@ public class ParallelFilteringProcessor<InputType> extends AbstractSimpleParalle
}
@Override
protected ProcessorInstruction<InputType> createInstruction(final InputType element) {
return new ProcessorInstruction<InputType>(this) {
protected AbstractProcessorInstruction<InputType> createInstruction(final InputType element) {
return new AbstractProcessorInstruction<InputType>(this, ProcessorInstructionPriority.Filtration) {
@Override
public InputType computeResult() {
if (filterCriterion.matches(element)) {
@@ -30,8 +30,8 @@ public class ParallelGroupedElementsValueExtractionProcessor<DataType, FunctionR
}
@Override
protected ProcessorInstruction<GroupedDataEntry<FunctionReturnType>> createInstruction(final GroupedDataEntry<DataType> element) {
return new ProcessorInstruction<GroupedDataEntry<FunctionReturnType>>(this) {
protected AbstractProcessorInstruction<GroupedDataEntry<FunctionReturnType>> createInstruction(final GroupedDataEntry<DataType> element) {
return new AbstractProcessorInstruction<GroupedDataEntry<FunctionReturnType>>(this, ProcessorInstructionPriority.Extraction) {
@Override
public GroupedDataEntry<FunctionReturnType> computeResult() {
FunctionReturnType value = extractionFunction.tryToInvoke(element.getDataEntry(), parameterProvider);
@@ -1,29 +0,0 @@
package com.sap.sse.datamining.impl.components;
public abstract class ProcessorInstruction<ResultType> implements Runnable {
private final AbstractPartitioningParallelProcessor<?, ?, ResultType> processor;
public ProcessorInstruction(AbstractPartitioningParallelProcessor<?, ?, ResultType> processor) {
this.processor = processor;
}
@Override
public void run() {
try {
ResultType result = computeResult();
if (processor.isResultValid(result) && !processor.isAborted()) {
processor.forwardResultToReceivers(result);
}
} catch (Exception e) {
if (!processor.isAborted() || !(e instanceof InterruptedException)) {
processor.onFailure(e);
}
} finally {
processor.getUnfinishedInstructionsCounter().getAndDecrement();
}
}
protected abstract ResultType computeResult() throws Exception;
}
@@ -0,0 +1,33 @@
package com.sap.sse.datamining.impl.components;
public class ProcessorInstructionPriority {
public static final ProcessorInstructionPriority Aggregation = new ProcessorInstructionPriority(0);
public static final ProcessorInstructionPriority Extraction = new ProcessorInstructionPriority(1);
public static final ProcessorInstructionPriority Filtration = new ProcessorInstructionPriority(1);
public static final ProcessorInstructionPriority Grouping = new ProcessorInstructionPriority(2);
private static final int retrievalBasePriority = Integer.MAX_VALUE;
/**
* Creates a priority for the given <code>retrieverLevel</code>, where the level <code>0</code>
* represents the first retriever in the chain.
*
* @param retrievalLevel The position of the retriever in its chain
* @return A priority for the given <code>retrieverLevel</code>
*/
public static ProcessorInstructionPriority createRetrievalPriority(int retrievalLevel) {
return new ProcessorInstructionPriority(retrievalBasePriority - retrievalLevel);
}
private final int intValue;
private ProcessorInstructionPriority(int intValue) {
this.intValue = intValue;
}
public int asIntValue() {
return intValue;
}
}
@@ -8,7 +8,8 @@ import java.util.concurrent.locks.ReentrantLock;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor;
import com.sap.sse.datamining.impl.components.ProcessorInstruction;
import com.sap.sse.datamining.impl.components.AbstractProcessorInstruction;
import com.sap.sse.datamining.impl.components.ProcessorInstructionPriority;
public abstract class AbstractParallelStoringAggregationProcessor<InputType, AggregatedType>
extends AbstractSimpleParallelProcessor<InputType, AggregatedType> {
@@ -27,8 +28,8 @@ public abstract class AbstractParallelStoringAggregationProcessor<InputType, Agg
}
@Override
protected ProcessorInstruction<AggregatedType> createInstruction(final InputType element) {
return new ProcessorInstruction<AggregatedType>(this) {
protected AbstractProcessorInstruction<AggregatedType> createInstruction(final InputType element) {
return new AbstractProcessorInstruction<AggregatedType>(this, ProcessorInstructionPriority.Aggregation) {
@Override
public AggregatedType computeResult() {
storeLock.lock();