Merge remote-tracking branch 'origin/master' into foiling

Conflicts:
	java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
	java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java
This commit is contained in:
Steffen Schaefer committed 2016-04-27 08:12:55 +02:00
commit e1f8dc06d5
166 files changed
+6263 -4576

No files matched your search

@@ -62,11 +62,11 @@ public class RaceGroupFactory {
return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, courseArea, series, configuration);
}
public Iterable<SeriesWithRows> getSeries(Leaderboard leaderboard) {
private Iterable<SeriesWithRows> getSeries(Leaderboard leaderboard) {
Map<Series, List<RaceColumn>> seriesToRaceColumns = getSeriesToRaceColumns(leaderboard);
Collection<SeriesWithRows> seriesWithRows = new ArrayList<>();
for (Series series : getSeriesIterable(leaderboard, seriesToRaceColumns)) {
seriesWithRows.add(new SeriesWithRowsImpl(series.getName(), series.isMedal(), getRows(series,
seriesWithRows.add(new SeriesWithRowsImpl(series.getName(), series.isMedal(), series.isFleetsCanRunInParallel(), getRows(series,
seriesToRaceColumns.get(series))));
}
return seriesWithRows;
@@ -80,6 +80,9 @@ public class RaceGroupFactory {
return seriesToRaceColumns.keySet();
}
/**
* @param raceColumns must be provided in the same order in which they appear in the series / leaderboard
*/
private Collection<RaceRow> getRows(Series series, List<RaceColumn> raceColumns) {
Collection<RaceRow> rows = new ArrayList<>();
for (Fleet fleet : series.getFleets()) {
@@ -94,16 +97,21 @@ public class RaceGroupFactory {
return series.isFirstColumnIsNonDiscardableCarryForward();
}
private Collection<RaceCell> getCells(String fleetName, List<RaceColumn> raceColumns, boolean isFirstRaceColumnVirtual) {
/**
* Delivers the race cell in the order of the {@link RaceColumn}s provided in {@code raceColumns}.
*/
private List<RaceCell> getCells(String fleetName, List<RaceColumn> raceColumns, boolean isFirstRaceColumnVirtual) {
boolean skippedFirst = false;
Collection<RaceCell> cells = new ArrayList<>();
List<RaceCell> cells = new ArrayList<>();
if (raceColumns != null) {
for (RaceColumn raceColumn : raceColumns) {
int zeroBasedIndexOfRaceInFleet = 0;
for (final RaceColumn raceColumn : raceColumns) {
if (isFirstRaceColumnVirtual && !skippedFirst) {
skippedFirst = true;
} else {
Fleet fleet = raceColumn.getFleetByName(fleetName);
cells.add(new RaceCellImpl(raceColumn.getName(), raceColumn.getRaceLog(fleet), raceColumn.getFactor(), raceColumn.getExplicitFactor()));
cells.add(new RaceCellImpl(raceColumn.getName(), raceColumn.getRaceLog(fleet),
raceColumn.getFactor(), raceColumn.getExplicitFactor(), zeroBasedIndexOfRaceInFleet++));
}
}
}
@@ -112,7 +120,9 @@ public class RaceGroupFactory {
/**
* Returns a series to race column mapping. If there are no series all race columns will
* be mapped from a default series.
* be mapped from a default series. The race column lists that appear as values in the result
* maintain the order in which the race column objects appear in the original {@link Series}
* or leaderboard.
*/
private Map<Series, List<RaceColumn>> getSeriesToRaceColumns(Leaderboard leaderboard) {
Map<Series, List<RaceColumn>> seriesToRaceColumns = new HashMap<>();
@@ -135,7 +145,7 @@ public class RaceGroupFactory {
private Series createDefaultSeries(Iterable<? extends Fleet> fleets) {
Series defaultSeries;
defaultSeries = new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, false,
defaultSeries = new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, false, true,
fleets, Collections.<String> emptyList(), null);
return defaultSeries;
}
@@ -80,7 +80,7 @@ public class CompetitorsResource extends AbstractSailingServerResource {
.type(MediaType.TEXT_PLAIN).build();
} else {
String jsonString = getCompetitorJSON(competitor).toJSONString();
response = Response.ok(jsonString, MediaType.APPLICATION_JSON).build();
response = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
return response;
}
@@ -110,7 +110,7 @@ public class CompetitorsResource extends AbstractSailingServerResource {
JSONObject teamJson = teamJsonSerializer.serialize(team);
String json = teamJson.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
/**
@@ -1,325 +1,356 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringEscapeUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.jaxrs.RestServletContainer;
import com.sap.sailing.server.gateway.serialization.NotJsonSerializableException;
import com.sap.sse.datamining.DataMiningServer;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.StatisticQueryDefinition;
import com.sap.sse.datamining.data.QueryResult;
import com.sap.sse.datamining.shared.DataMiningSession;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.impl.CompoundGroupKey;
import com.sap.sse.datamining.shared.impl.GenericGroupKey;
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
import com.sap.sse.datamining.shared.impl.UUIDDataMiningSession;
import com.sap.sse.datamining.shared.impl.dto.FunctionDTO;
import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinitionDTO;
/**
* REST-API to run predefined queries and get the result as JSON.
* @author Lennart Hensler (D054527)
*/
@Path("/v1/datamining")
public class DataMiningResource extends AbstractSailingServerResource {
private static final String AVG_SPEED_PER_COMPETITOR_LEG_TYPE = "AvgSpeed_Per_Competitor-LegType";
private static final String SUM_DISTANCE_PER_COMPETITOR_LEG_TYPE = "SumDistance_Per_Competitor-LegType";
private static final String SUM_MANEUVERS_PER_COMPETITOR = "SumManeuvers_Per_Competitor";
private static final Function<Distance, Number> distanceMetersExtractor = (distance) -> distance.getMeters();
public DataMiningServer getDataMiningServer() {
@SuppressWarnings("unchecked")
ServiceTracker<DataMiningServer, DataMiningServer> dataMiningServerTracker = (ServiceTracker<DataMiningServer, DataMiningServer>) getServletContext().getAttribute(RestServletContainer.DATA_MINING_SERVER_TRACKER_NAME);
return dataMiningServerTracker.getService();
}
private Response getBadIdentifierErrorResponse(String identifier) {
return Response.status(Status.NOT_FOUND).entity("Couldn't find a predefined query with the identifier '" + identifier + "'.")
.type(MediaType.TEXT_PLAIN).build();
}
private Response getBadQueryDefinitionErrorResponse(ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO) {
return Response.status(Status.BAD_REQUEST).entity("Couldn't create a query for definition '" + StringEscapeUtils.escapeHtml(queryDefinitionDTO.toString()) + "'.")
.type(MediaType.TEXT_PLAIN).build();
}
private Response getNoDataFoundErrorResponse() {
return Response.status(Status.NOT_FOUND).entity("No data found.").type(MediaType.TEXT_PLAIN).build();
}
private Response getNotSerializableErrorResponse(Class<?> type) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Can't serialize values of type '" + type.getName() + "'.").
type(MediaType.TEXT_PLAIN).build();
}
// @GET
// @Produces("application/json;charset=UTF-8")
// public Response getPredefinedQueries() {
// return predefinedQueryIdentifiersToJSON(getDataMiningServer().getPredefinedQueryIdentifiers());
// }
public Response predefinedQueryIdentifiersToJSON(Iterable<PredefinedQueryIdentifier> identifiers) {
JSONArray predefinedQueryNames = new JSONArray();
for (PredefinedQueryIdentifier identifier : identifiers) {
JSONObject jsonId = new JSONObject();
jsonId.put("Identifier", identifier.getIdentifier());
jsonId.put("Description", identifier.getDescription());
predefinedQueryNames.add(jsonId);
}
String json = predefinedQueryNames.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
// @GET
// @Produces("application/json;charset=UTF-8")
// @Path("{identifier}")
// public Response runPredefinedQuery(@PathParam("identifier") String identifier) {
// Response response;
// ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(identifier);
// if (queryDefinitionDTO == null) {
// response = getBadIdentifierErrorResponse(identifier);
// } else {
// response = runQuery(queryDefinitionDTO);
// }
// return response;
// }
public Response avgSpeedPerCompetitorAndLegType(String regattaName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(AVG_SPEED_PER_COMPETITOR_LEG_TYPE);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(AVG_SPEED_PER_COMPETITOR_LEG_TYPE);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, null, null, "kn", 2);
}
return response;
}
public Response avgSpeedPerCompetitorAndLegType(String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(AVG_SPEED_PER_COMPETITOR_LEG_TYPE);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(AVG_SPEED_PER_COMPETITOR_LEG_TYPE);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, null, "kn", 2);
}
return response;
}
public Response sumDistancePerCompetitorAndLegType(String regattaName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(SUM_DISTANCE_PER_COMPETITOR_LEG_TYPE);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(SUM_DISTANCE_PER_COMPETITOR_LEG_TYPE);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, null, distanceMetersExtractor, "m", 2);
}
return response;
}
public Response sumDistancePerCompetitorAndLegType(String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(SUM_DISTANCE_PER_COMPETITOR_LEG_TYPE);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(SUM_DISTANCE_PER_COMPETITOR_LEG_TYPE);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, distanceMetersExtractor, "m", 2);
}
return response;
}
public Response sumManeuversPerCompetitor(String regattaName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(SUM_MANEUVERS_PER_COMPETITOR);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(SUM_MANEUVERS_PER_COMPETITOR);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, null, null, null, 0);
}
return response;
}
public Response sumManeuversPerCompetitor(String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(SUM_MANEUVERS_PER_COMPETITOR);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(SUM_MANEUVERS_PER_COMPETITOR);
} else {
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, null, null, 0);
}
return response;
}
private ModifiableStatisticQueryDefinitionDTO getPredefinedQuery(String identifier) {
DataMiningServer dataMiningServer = getDataMiningServer();
PredefinedQueryIdentifier queryIdentifier = new PredefinedQueryIdentifier(identifier, "");
return dataMiningServer.getPredefinedQueryDefinitionDTO(queryIdentifier);
}
private <ResultType> Response runQuery(ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO, String regattaName, String raceName,
Function<ResultType, Number> numberExtractor, String resultUnit, int resultPlaces) {
Response response;
DataMiningServer dataMiningServer = getDataMiningServer();
StatisticQueryDefinition<?, ?, ?, ?> queryDefinition = dataMiningServer.getQueryDefinitionForDTO(queryDefinitionDTO);
Query<?> query = queryDefinition == null ? null : dataMiningServer.createQuery(queryDefinition);
if (query == null) {
response = getBadQueryDefinitionErrorResponse(queryDefinitionDTO);
} else {
DataMiningSession session = new UUIDDataMiningSession(UUID.randomUUID());
long requestTimepoint = System.currentTimeMillis();
@SuppressWarnings("unchecked")
QueryResult<ResultType> result = (QueryResult<ResultType>) dataMiningServer.runNewQueryAndAbortPreviousQueries(session, query);
if (result == null || result.isEmpty()) {
response = getNoDataFoundErrorResponse();
} else {
try {
JSONObject jsonResult = new JSONObject();
jsonResult.put("state", result.getState());
jsonResult.put("requestTimepoint", requestTimepoint);
jsonResult.put("calculationDuration-s", roundDouble(result.getCalculationTimeInSeconds(), 2));
jsonResult.put("description", result.getResultSignifier());
if (regattaName != null && !regattaName.isEmpty()) {
jsonResult.put("regatta", regattaName);
}
if (raceName != null && !raceName.isEmpty()) {
jsonResult.put("race", raceName);
}
jsonResult.put("resultUnit", resultUnit != null && !resultUnit.isEmpty() ? resultUnit : "None");
jsonResult.put("results", resultValuesToJSON(result, numberExtractor, resultPlaces));
response = Response.ok(jsonResult.toJSONString(), MediaType.APPLICATION_JSON).build();
} catch (NotJsonSerializableException e) {
response = getNotSerializableErrorResponse(e.getNotSerializableClass());
}
}
}
return response;
}
private <ResultType> JSONArray resultValuesToJSON(QueryResult<ResultType> result, Function<ResultType, Number> numberExtractor,
int places) throws NotJsonSerializableException {
if (!Number.class.isAssignableFrom(result.getResultType()) && numberExtractor == null) {
throw new NotJsonSerializableException(result.getResultType());
}
// TODO see Bug 3334
Map<GroupKey, ResultType> values = result.getResults();
JSONArray jsonResultValues = new JSONArray();
for (GroupKey groupKey : values.keySet()) {
JSONObject jsonResultEntry = new JSONObject();
jsonResultEntry.put("groupKey", groupKeyToJSON(groupKey));
Number value = numberExtractor != null ? numberExtractor.apply(values.get(groupKey)) : (Number) values.get(groupKey);
jsonResultEntry.put("value", roundDouble(value.doubleValue(), places));
jsonResultValues.add(jsonResultEntry);
}
return jsonResultValues;
}
private JSONArray groupKeyToJSON(GroupKey groupKey) throws NotJsonSerializableException {
// TODO Convert the concrete key values and not just the string represantation
JSONArray jsonResultEntryGroupKeys = new JSONArray();
if (groupKey instanceof GenericGroupKey<?>) {
jsonResultEntryGroupKeys.add(simpleGroupKeyToJSON(groupKey));
} else if (groupKey instanceof CompoundGroupKey) {
for (GroupKey key : groupKey.getKeys()) {
jsonResultEntryGroupKeys.add(groupKeyToJSON(key));
}
} else {
throw new NotJsonSerializableException(groupKey.getClass());
}
return jsonResultEntryGroupKeys;
}
private Object simpleGroupKeyToJSON(GroupKey groupKey) {
return groupKey.asString();
}
}
package com.sap.sailing.server.gateway.jaxrs.api;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang.StringEscapeUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.datamining.SailingPredefinedQueries;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.jaxrs.RestServletContainer;
import com.sap.sailing.server.gateway.serialization.NotJsonSerializableException;
import com.sap.sse.datamining.DataMiningServer;
import com.sap.sse.datamining.Query;
import com.sap.sse.datamining.StatisticQueryDefinition;
import com.sap.sse.datamining.data.QueryResult;
import com.sap.sse.datamining.shared.DataMiningSession;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.impl.CompoundGroupKey;
import com.sap.sse.datamining.shared.impl.GenericGroupKey;
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
import com.sap.sse.datamining.shared.impl.UUIDDataMiningSession;
import com.sap.sse.datamining.shared.impl.dto.FunctionDTO;
import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinitionDTO;
/**
* REST-API to run predefined queries and get the result as JSON.
* @author Lennart Hensler (D054527)
*/
@Path("/v1/datamining")
public class DataMiningResource extends AbstractSailingServerResource {
private SailingPredefinedQueries predefinedDataMiningQueries;
private static final Function<Distance, Number> distanceMetersExtractor = (distance) -> distance.getMeters();
public final static String QUERY_AVERAGE_SPEED_PER_REGATTA_RACE = "AvgSpeed_Per_Regatta-Race";
public final static String QUERY_AVERAGE_SPEED_PER_COMPETITOR_LEGTYPE = "AvgSpeed_Per_Competitor-LegType";
public final static String QUERY_AVERAGE_SPEED_PER_COMPETITOR = "AvgSpeed_Per_Competitor";
public final static String QUERY_DISTANCE_TRAVELED_PER_COMPETITOR_LEGTYPE = "DistanceTraveled_Per_Competitor-LegType";
public final static String QUERY_DISTANCE_TRAVELED_PER_COMPETITOR = "DistanceTraveled_Per_Competitor";
public final static String QUERY_MANEUVERS_PER_COMPETITOR = "Maneuvers_Per_Competitor";
public List<PredefinedQueryIdentifier> getPredefinedRegattaDataMiningQueries() {
return getPredefinedQueriesByNames(
QUERY_AVERAGE_SPEED_PER_COMPETITOR_LEGTYPE,
QUERY_AVERAGE_SPEED_PER_COMPETITOR,
QUERY_DISTANCE_TRAVELED_PER_COMPETITOR_LEGTYPE,
QUERY_DISTANCE_TRAVELED_PER_COMPETITOR,
QUERY_MANEUVERS_PER_COMPETITOR);
}
public List<PredefinedQueryIdentifier> getPredefinedRaceDataMiningQueries() {
return getPredefinedQueriesByNames(
QUERY_AVERAGE_SPEED_PER_REGATTA_RACE,
QUERY_AVERAGE_SPEED_PER_COMPETITOR_LEGTYPE,
QUERY_AVERAGE_SPEED_PER_COMPETITOR,
QUERY_DISTANCE_TRAVELED_PER_COMPETITOR_LEGTYPE,
QUERY_DISTANCE_TRAVELED_PER_COMPETITOR,
QUERY_MANEUVERS_PER_COMPETITOR);
}
public List<PredefinedQueryIdentifier> getPredefinedQueriesByNames(String... names) {
List<PredefinedQueryIdentifier> result = new ArrayList<>();
SailingPredefinedQueries predefinedMiningQueries = getPredefinedMiningQueries();
for(PredefinedQueryIdentifier id: predefinedMiningQueries.getQueries().keySet()) {
for(String name: names) {
if(id.getIdentifier().equals(name)) {
result.add(id);
}
}
}
return result;
}
public DataMiningServer getDataMiningServer() {
@SuppressWarnings("unchecked")
ServiceTracker<DataMiningServer, DataMiningServer> dataMiningServerTracker = (ServiceTracker<DataMiningServer, DataMiningServer>) getServletContext().getAttribute(RestServletContainer.DATA_MINING_SERVER_TRACKER_NAME);
return dataMiningServerTracker.getService();
}
private Response getBadIdentifierErrorResponse(String identifier) {
return Response.status(Status.NOT_FOUND).entity("Couldn't find a predefined query with the identifier '" + identifier + "'.")
.type(MediaType.TEXT_PLAIN).build();
}
private Response getBadQueryDefinitionErrorResponse(ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO) {
return Response.status(Status.BAD_REQUEST).entity("Couldn't create a query for definition '" + StringEscapeUtils.escapeHtml(queryDefinitionDTO.toString()) + "'.")
.type(MediaType.TEXT_PLAIN).build();
}
private Response getNoDataFoundErrorResponse() {
return Response.status(Status.NOT_FOUND).entity("No data found.").type(MediaType.TEXT_PLAIN).build();
}
private Response getNotSerializableErrorResponse(Class<?> type) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Can't serialize values of type '" + type.getName() + "'.").
type(MediaType.TEXT_PLAIN).build();
}
private SailingPredefinedQueries getPredefinedMiningQueries() {
if (predefinedDataMiningQueries == null) {
predefinedDataMiningQueries = new SailingPredefinedQueries();
}
return predefinedDataMiningQueries;
}
public Response predefinedQueryIdentifiersToJSON(Iterable<PredefinedQueryIdentifier> identifiers) {
JSONArray predefinedQueryNames = new JSONArray();
for (PredefinedQueryIdentifier identifier : identifiers) {
JSONObject jsonId = new JSONObject();
jsonId.put("Identifier", identifier.getIdentifier());
jsonId.put("Description", identifier.getDescription());
predefinedQueryNames.add(jsonId);
}
String json = predefinedQueryNames.toJSONString();
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
public Response avgSpeedPerCompetitorAndLegType(String regattaName) {
return avgSpeedPerCompetitorQuery(SailingPredefinedQueries.QUERY_AVERAGE_SPEED_PER_COMPETITOR_LEGTYPE, regattaName);
}
public Response avgSpeedPerCompetitor(String regattaName) {
return avgSpeedPerCompetitorQuery(SailingPredefinedQueries.QUERY_AVERAGE_SPEED_PER_COMPETITOR, regattaName);
}
public Response avgSpeedPerCompetitorAndLegType(String regattaName, String raceName) {
return avgSpeedPerCompetitorQuery(SailingPredefinedQueries.QUERY_AVERAGE_SPEED_PER_COMPETITOR_LEGTYPE, regattaName, raceName);
}
public Response avgSpeedPerCompetitor(String regattaName, String raceName) {
return avgSpeedPerCompetitorQuery(SailingPredefinedQueries.QUERY_AVERAGE_SPEED_PER_COMPETITOR, regattaName, raceName);
}
private Response avgSpeedPerCompetitorQuery(String predefinedQueriesName, String regattaName) {
return avgSpeedPerCompetitorQuery(predefinedQueriesName, regattaName, null);
}
private Response avgSpeedPerCompetitorQuery(String predefinedQueriesName, String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(predefinedQueriesName);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(predefinedQueriesName);
} else {
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
if(raceName != null) {
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
}
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, null, "kn", 2);
}
return response;
}
public Response sumDistanceTraveledPerCompetitor(String regattaName) {
return sumDistanceTraveledPerCompetitorQuery(SailingPredefinedQueries.QUERY_DISTANCE_TRAVELED_PER_COMPETITOR, regattaName);
}
public Response sumDistanceTraveledPerCompetitorAndLegType(String regattaName) {
return sumDistanceTraveledPerCompetitorQuery(SailingPredefinedQueries.QUERY_DISTANCE_TRAVELED_PER_COMPETITOR_LEGTYPE, regattaName);
}
public Response sumDistanceTraveledPerCompetitor(String regattaName, String raceName) {
return sumDistanceTraveledPerCompetitorQuery(SailingPredefinedQueries.QUERY_DISTANCE_TRAVELED_PER_COMPETITOR, regattaName, raceName);
}
public Response sumDistanceTraveledPerCompetitorAndLegType(String regattaName, String raceName) {
return sumDistanceTraveledPerCompetitorQuery(SailingPredefinedQueries.QUERY_DISTANCE_TRAVELED_PER_COMPETITOR_LEGTYPE, regattaName, raceName);
}
private Response sumDistanceTraveledPerCompetitorQuery(String predefinedQueriesName, String regattaName) {
return sumDistanceTraveledPerCompetitorQuery(predefinedQueriesName, regattaName, null);
}
private Response sumDistanceTraveledPerCompetitorQuery(String predefinedQueriesName, String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(predefinedQueriesName);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(predefinedQueriesName);
} else {
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
if(raceName != null) {
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
}
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, distanceMetersExtractor, "m", 2);
}
return response;
}
public Response sumManeuversPerCompetitor(String regattaName) {
return sumManeuversPerCompetitorQuery(SailingPredefinedQueries.QUERY_MANEUVERS_PER_COMPETITOR, regattaName);
}
public Response sumManeuversPerCompetitor(String regattaName, String raceName) {
return sumManeuversPerCompetitorQuery(SailingPredefinedQueries.QUERY_MANEUVERS_PER_COMPETITOR, regattaName, raceName);
}
private Response sumManeuversPerCompetitorQuery(String predefinedQueriesName, String regattaName) {
return sumManeuversPerCompetitorQuery(predefinedQueriesName, regattaName, null);
}
private Response sumManeuversPerCompetitorQuery(String predefinedQueriesName, String regattaName, String raceName) {
Response response;
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = getPredefinedQuery(predefinedQueriesName);
if (queryDefinitionDTO == null) {
response = getBadIdentifierErrorResponse(predefinedQueriesName);
} else {
HashMap<FunctionDTO, HashSet<? extends Serializable>> race_FilterSelection = new HashMap<>();
FunctionDTO getRegattaName = new FunctionDTO(true, "getRegatta().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRegattaName_FilterSelection = new HashSet<>();
getRegattaName_FilterSelection.add(regattaName);
race_FilterSelection.put(getRegattaName, getRegattaName_FilterSelection);
if (raceName != null) {
FunctionDTO getRaceName = new FunctionDTO(true, "getRace().getName()", HasTrackedRaceContext.class.getName(), String.class.getName(), new ArrayList<String>(), "", 0);
HashSet<Serializable> getRaceName_FilterSelection = new HashSet<>();
getRaceName_FilterSelection.add(raceName);
race_FilterSelection.put(getRaceName, getRaceName_FilterSelection);
}
queryDefinitionDTO.setFilterSelectionFor(queryDefinitionDTO.getDataRetrieverChainDefinition().getRetrieverLevel(2), race_FilterSelection);
response = runQuery(queryDefinitionDTO, regattaName, raceName, null, null, 0);
}
return response;
}
private ModifiableStatisticQueryDefinitionDTO getPredefinedQuery(String identifier) {
DataMiningServer dataMiningServer = getDataMiningServer();
PredefinedQueryIdentifier queryIdentifier = new PredefinedQueryIdentifier(identifier, "");
return dataMiningServer.getPredefinedQueryDefinitionDTO(queryIdentifier);
}
private <ResultType> Response runQuery(ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO, String regattaName, String raceName,
Function<ResultType, Number> numberExtractor, String resultUnit, int resultPlaces) {
Response response;
DataMiningServer dataMiningServer = getDataMiningServer();
StatisticQueryDefinition<?, ?, ?, ?> queryDefinition = dataMiningServer.getQueryDefinitionForDTO(queryDefinitionDTO);
Query<?> query = queryDefinition == null ? null : dataMiningServer.createQuery(queryDefinition);
if (query == null) {
response = getBadQueryDefinitionErrorResponse(queryDefinitionDTO);
} else {
DataMiningSession session = new UUIDDataMiningSession(UUID.randomUUID());
long requestTimepoint = System.currentTimeMillis();
@SuppressWarnings("unchecked")
QueryResult<ResultType> result = (QueryResult<ResultType>) dataMiningServer.runNewQueryAndAbortPreviousQueries(session, query);
if (result == null || result.isEmpty()) {
response = getNoDataFoundErrorResponse();
} else {
try {
JSONObject jsonResult = new JSONObject();
jsonResult.put("state", result.getState());
jsonResult.put("requestTimepoint", requestTimepoint);
jsonResult.put("calculationDuration-s", roundDouble(result.getCalculationTimeInSeconds(), 2));
jsonResult.put("description", result.getResultSignifier());
if (regattaName != null && !regattaName.isEmpty()) {
jsonResult.put("regatta", regattaName);
}
if (raceName != null && !raceName.isEmpty()) {
jsonResult.put("race", raceName);
}
jsonResult.put("resultUnit", resultUnit != null && !resultUnit.isEmpty() ? resultUnit : "None");
jsonResult.put("results", resultValuesToJSON(result, numberExtractor, resultPlaces));
response = Response.ok(jsonResult.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NotJsonSerializableException e) {
response = getNotSerializableErrorResponse(e.getNotSerializableClass());
}
}
}
return response;
}
private <ResultType> JSONArray resultValuesToJSON(QueryResult<ResultType> result, Function<ResultType, Number> numberExtractor,
int places) throws NotJsonSerializableException {
if (!Number.class.isAssignableFrom(result.getResultType()) && numberExtractor == null) {
throw new NotJsonSerializableException(result.getResultType());
}
// TODO see Bug 3334
Map<GroupKey, ResultType> values = result.getResults();
JSONArray jsonResultValues = new JSONArray();
for (GroupKey groupKey : values.keySet()) {
JSONObject jsonResultEntry = new JSONObject();
jsonResultEntry.put("groupKey", groupKeyToJSON(groupKey));
Number value = numberExtractor != null ? numberExtractor.apply(values.get(groupKey)) : (Number) values.get(groupKey);
jsonResultEntry.put("value", roundDouble(value.doubleValue(), places));
jsonResultValues.add(jsonResultEntry);
}
return jsonResultValues;
}
private JSONArray groupKeyToJSON(GroupKey groupKey) throws NotJsonSerializableException {
// TODO Convert the concrete key values and not just the string representation
JSONArray jsonResultEntryGroupKeys = new JSONArray();
if (groupKey instanceof GenericGroupKey<?>) {
jsonResultEntryGroupKeys.add(simpleGroupKeyToJSON(groupKey));
} else if (groupKey instanceof CompoundGroupKey) {
for (GroupKey key : groupKey.getKeys()) {
jsonResultEntryGroupKeys.add(groupKeyToJSON(key));
}
} else {
throw new NotJsonSerializableException(groupKey.getClass());
}
return jsonResultEntryGroupKeys;
}
private Object simpleGroupKeyToJSON(GroupKey groupKey) {
return groupKey.asString();
}
}
@@ -46,7 +46,7 @@ public class EventsResource extends AbstractSailingServerResource {
}
}
String json = result.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
@GET
@@ -71,7 +71,7 @@ public class EventsResource extends AbstractSailingServerResource {
JSONObject eventJson = eventSerializer.serialize(event);
String json = eventJson.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build();
response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
return response;
}
@@ -98,7 +98,7 @@ public class EventsResource extends AbstractSailingServerResource {
filterByLeaderboard, filterByDayOffset, getService());
JSONObject raceStatesJson = eventRaceStatesSerializer.serialize(new Pair<Event, Iterable<Leaderboard>>(event, getService().getLeaderboards().values()));
String json = raceStatesJson.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build();
response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
return response;
}
@@ -13,6 +13,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
@@ -65,7 +66,7 @@ public class FileStorageResource extends AbstractSailingServerResource {
getService().getFileStorageManagementService().getActiveFileStorageService()
.removeFile(new URI(uri));
result.put("status", Status.OK.name());
response = Response.ok().entity(result.toJSONString()).build();
response = Response.ok().entity(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NoCorrespondingServiceRegisteredException | OperationFailedException | InvalidPropertiesException
| URISyntaxException | IOException e) {
final String errorMessage = "Could not delete file with URI "+uri+": "+e.getMessage();
@@ -52,7 +52,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
// header option is set to allow communication between two sapsailing servers, especially for
// the master data import functionality
return Response.ok(json, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*").build();
return Response.ok(json).header("Access-Control-Allow-Origin", "*").header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
@GET
@@ -154,7 +154,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource {
}
}
String json = jsonLeaderboardGroup.toJSONString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build();
response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
return response;
}
@@ -104,7 +104,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
jsonLeaderboards.add(leaderboardName);
}
String json = jsonLeaderboards.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
@GET
@@ -138,7 +138,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
jsonLeaderboard.writeJSONString(sw);
String json = sw.getBuffer().toString();
response = Response.ok(json, MediaType.APPLICATION_JSON).build();
response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
} catch (NoWindException | InterruptedException | ExecutionException | IOException e) {
response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
.type(MediaType.TEXT_PLAIN).build();
@@ -509,7 +509,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
} else {
JSONObject json = CompetitorsResource.getCompetitorJSON(competitor);
json.put("displayName", leaderboard.getDisplayName(competitor));
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build();
response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
return response;
}
@@ -580,7 +580,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
}
JSONObject result = new JSONObject();
result.put("marks", array);
return Response.ok(result.toJSONString(), MediaType.APPLICATION_JSON).build();
return Response.ok(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
private final MarkJsonSerializer markSerializer = new MarkJsonSerializer();
@@ -671,7 +671,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
.type(MediaType.TEXT_PLAIN).build();
} else {
final JSONObject json = getJsonForColumnFactors(leaderboard, raceColumns);
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build();
response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}
return response;
@@ -725,7 +725,7 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
} else {
raceColumn.setFactor(explicitFactor);
final JSONObject json = getJsonForColumnFactors(leaderboard, Collections.singleton(raceColumn));
response = Response.ok(json.toJSONString(), MediaType.APPLICATION_JSON).build();
response = Response.ok(json.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}
return response;
@@ -3,6 +3,7 @@ package com.sap.sailing.server.gateway.jaxrs.api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
@@ -12,6 +13,6 @@ public class PingResource extends AbstractSailingServerResource {
@GET
@Produces("application/json;charset=UTF-8")
public Response ping() {
return Response.ok().build();
return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}
@@ -170,7 +170,7 @@ public class PolarResource extends AbstractSailingServerResource {
} finally {
maneuverBasedWindEstimationTrackImpl.unlockAfterRead();
}
response = Response.ok(resultAsJson.toJSONString(), MediaType.APPLICATION_JSON).build();
response = Response.ok(resultAsJson.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}
return response;
@@ -43,6 +43,6 @@ public class SearchResource extends AbstractSailingServerResource {
for (LeaderboardSearchResult searchResult : searchResults) {
jsonSearchResults.add(serializer.serialize(searchResult));
}
return Response.ok(jsonSearchResults.toJSONString(), MediaType.APPLICATION_JSON).build();
return Response.ok(jsonSearchResults.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
}
}