Merge remote-tracking branch 'origin/newhome' into newhome

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/app/SmartphoneApplicationClientFactory.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/start/SmartphoneStartView.java
This commit is contained in:
fmittag committed 2014-06-05 14:07:15 +02:00
commit 4aa0363be1
53 files changed
+4052 -3008

No files matched your search

@@ -57,7 +57,13 @@ public class AbstractPosition implements Position {
@Override
public Distance getDistance(Position p) {
return new CentralAngleDistance(getCentralAngleRad(p));
final Distance result;
if (p == this || this.equals(p)) {
result = Distance.NULL;
} else {
result = new CentralAngleDistance(getCentralAngleRad(p));
}
return result;
}
@Override
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.base;
import com.sap.sse.common.Named;
public interface LeaderboardBase extends Named {
/**
* If a display name for the leaderboard has been defined,
* this method returns it; otherwise, <code>null</code> is returned.
*/
String getDisplayName();
}
@@ -0,0 +1,38 @@
package com.sap.sailing.domain.base;
import com.sap.sse.common.search.Hit;
/**
* A generalization of <code>LeaderboardSearchResult</code> which has references to full-blown leaderboards, a regatta,
* full leaderboard groups and the entire event; this form requires only the respective <code>...Base</code> flavors of
* events, leaderboards and leaderboard groups which allows for the creation of a copy in a server that has been
* receiving a search result from some other (remote) server, e.g., through JSON deserialization.
*
* @author Axel Uhl (D043530)
*
*/
public interface LeaderboardSearchResultBase extends Hit {
/**
* The leaderboard that matches the search query.
*/
LeaderboardBase getLeaderboard();
String getRegattaName();
String getBoatClassName();
/**
* If the leaderboard is part of one or more leaderboard groups, the leaderboard groups; an empty but
* valid (non-<code>null</code>) collection otherwise.
*/
Iterable<? extends LeaderboardGroupBase> getLeaderboardGroups();
/**
* If there is a connection between an {@link EventBase} and the {@link #getLeaderboard() leaderboard}, e.g.,
* because the {@link EventBase} has a {@link EventBase#getLeaderboardGroups() leaderboard group} that is part of
* {@link #getLeaderboardGroups()} or the event has a course area attached to its {@link EventBase#getVenue() venue}
* that is referenced by the leaderboard as its {@link Leaderboard#getDefaultCourseArea() default course area}, the
* event is returned; <code>null</code> otherwise.
*/
EventBase getEvent();
}
@@ -0,0 +1,42 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
/**
* This is what you get when you search a <code>RacingEventService</code> with its <code>search(...)</code> method. It
* delivers the "real things," meaning that the leaderboards and events etc. are those objects currently living in the
* server instance. This is richer than the de-serialized {@link LeaderboardSearchResultBase} objects you get when
* fetching search results from remote instances, such as those described by a {@link RemoteSailingServerReference}.
* However, for putting them into a DTO to transfer them to a web client, both are fine.
*
* @author Axel Uhl (D043530)
*
*/
public interface LeaderboardSearchResult extends LeaderboardSearchResultBase {
/**
* The leaderboard that matches the search query.
*/
Leaderboard getLeaderboard();
/**
* Returns the {@link RegattaLeaderboard#getRegatta() regatta leaderboard's regatta} if the
* {@link #getLeaderboard() leaderboard} is a regatta leaderboard, or <code>null</code> otherwise.
*/
Regatta getRegatta();
/**
* If the leaderboard is part of one or more leaderboard groups, the leaderboard groups; an empty but
* valid (non-<code>null</code>) collection otherwise.
*/
Iterable<LeaderboardGroup> getLeaderboardGroups();
/**
* If there is a connection between an {@link Event} and the {@link #getLeaderboard() leaderboard}, e.g., because
* the {@link Event} has a {@link Event#getLeaderboardGroups() leaderboard group} that is part of {@link #getLeaderboardGroups()} or
* the event has a course area attached to its {@link Event#getVenue() venue} that is referenced by the leaderboard as
* its {@link Leaderboard#getDefaultCourseArea() default course area}, the event is returned; <code>null</code> otherwise.
*/
Event getEvent();
}
@@ -0,0 +1,26 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.LeaderboardBase;
public class LeaderboardBaseImpl implements LeaderboardBase {
private static final long serialVersionUID = -5332972141344617372L;
private final String name;
private final String displayName;
public LeaderboardBaseImpl(String name, String displayName) {
super();
this.name = name;
this.displayName = displayName;
}
@Override
public String getName() {
return name;
}
@Override
public String getDisplayName() {
return displayName;
}
}
@@ -0,0 +1,60 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.leaderboard.Leaderboard;
/**
* The simplified version of a {@link LeaderboardSearchResult}, after JSON deserialization on a remote server. In particular,
* the {@link Leaderboard} object is most certainly not available on the receiving end. {@link EventBase} and {@link LeaderboardGroupBase}
* objects are
*
* @author Axel Uhl (D043530)
*
*/
public class LeaderboardSearchResultBaseImpl implements LeaderboardSearchResultBase {
private final LeaderboardBase leaderboard;
private final String regattaName;
private final Iterable<? extends LeaderboardGroupBase> leaderboardGroups;
private final EventBase event;
private final String boatClassName;
public LeaderboardSearchResultBaseImpl(LeaderboardBase leaderboard, String regattaName,
String boatClassName, Iterable<? extends LeaderboardGroupBase> leaderboardGroups, EventBase event) {
super();
this.leaderboard = leaderboard;
this.regattaName = regattaName;
this.boatClassName = boatClassName;
this.leaderboardGroups = leaderboardGroups;
this.event = event;
}
@Override
public LeaderboardBase getLeaderboard() {
return leaderboard;
}
@Override
public String getRegattaName() {
return regattaName;
}
@Override
public String getBoatClassName() {
return boatClassName;
}
@Override
public Iterable<? extends LeaderboardGroupBase> getLeaderboardGroups() {
return leaderboardGroups;
}
@Override
public EventBase getEvent() {
return event;
}
}
@@ -0,0 +1,83 @@
package com.sap.sailing.domain.base.impl;
import java.util.Collections;
import java.util.Set;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.tracking.TrackedRace;
public class LeaderboardSearchResultImpl implements LeaderboardSearchResult {
private final Leaderboard leaderboard;
private final Event event;
private final Set<LeaderboardGroup> leaderboardGroups;
public LeaderboardSearchResultImpl(Leaderboard leaderboard, Event event, Set<LeaderboardGroup> leaderboardGroups) {
this.leaderboard = leaderboard;
if (leaderboardGroups == null) {
this.leaderboardGroups = Collections.emptySet();
} else {
this.leaderboardGroups = leaderboardGroups;
}
this.event = event;
}
@Override
public Regatta getRegatta() {
final Regatta regatta;
if (leaderboard instanceof RegattaLeaderboard) {
regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
} else {
regatta = null;
}
return regatta;
}
@Override
public String getRegattaName() {
return getRegatta() != null ? getRegatta().getName() : null;
}
@Override
public Leaderboard getLeaderboard() {
return leaderboard;
}
@Override
public Event getEvent() {
return event;
}
@Override
public Iterable<LeaderboardGroup> getLeaderboardGroups() {
return leaderboardGroups;
}
@Override
public String getBoatClassName() {
BoatClass boatClass = getBoatClass();
return boatClass == null ? null : boatClass.getName();
}
public BoatClass getBoatClass() {
final BoatClass boatClass;
if (getRegatta() != null) {
boatClass = getRegatta().getBoatClass();
} else {
boatClass = getBoatClassFromTrackedRaces();
}
return boatClass;
}
private BoatClass getBoatClassFromTrackedRaces() {
for (TrackedRace trackedRace : getLeaderboard().getTrackedRaces()) {
return trackedRace.getRace().getBoatClass();
}
return null;
}
}
@@ -9,6 +9,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.LeaderboardBase;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.Series;
@@ -25,7 +26,6 @@ import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sse.common.Named;
import com.sap.sse.common.Util;
/**
@@ -43,7 +43,7 @@ import com.sap.sse.common.Util;
* @author Axel Uhl (d043530)
*
*/
public interface Leaderboard extends Named {
public interface Leaderboard extends LeaderboardBase {
/**
* If the leaderboard is a "matrix" with the cells being defined by a competitor / race "coordinate,"
* then this interface defines the structure of the "cells."
@@ -300,12 +300,6 @@ public interface Leaderboard extends Named {
void setDisplayName(Competitor competitor, String displayName);
/**
* If a display name for the leaderboard has been defined,
* this method returns it; otherwise, <code>null</code> is returned.
*/
String getDisplayName();
void setDisplayName(String displayName);
/**
File diff suppressed because it is too large. Load diff
@@ -53,7 +53,7 @@
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Search.html com.sap.sailing.gwt.ui.Search -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m"/>
</launchConfiguration>
+50
View File
@@ -0,0 +1,50 @@
<!doctype html>
<!-- The DOCTYPE declaration above will set the -->
<!-- browser's rendering engine into -->
<!-- "Standards Mode". Replacing this declaration -->
<!-- with a "Quirks Mode" doctype may lead to some -->
<!-- differences in layout. -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -->
<!-- Consider inlining CSS to reduce the number of requested files -->
<!-- -->
<link rel="shortcut icon" type="image/x-icon" href="images/sap.ico" />
<!-- -->
<!-- Any title is fine -->
<!-- -->
<title>SAP Sailing Analytics Search Demonstration</title>
<!-- -->
<!-- This script loads your compiled module. -->
<!-- If you add any GWT meta tags, they must -->
<!-- be added before this line. -->
<!-- -->
<script type="text/javascript" language="javascript" src="com.sap.sailing.gwt.ui.Search/com.sap.sailing.gwt.ui.Search.nocache.js"></script>
</head>
<!-- -->
<!-- The body can have arbitrary html, or -->
<!-- you can leave the body empty if you want -->
<!-- to create a completely dynamic UI. -->
<!-- -->
<body>
<!-- OPTIONAL: include this if you want history support -->
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
</body>
</html>
@@ -7,9 +7,9 @@ import com.sap.sailing.gwt.home.client.place.event.EventView;
import com.sap.sailing.gwt.home.client.place.events.EventsActivity;
import com.sap.sailing.gwt.home.client.place.events.EventsView;
import com.sap.sailing.gwt.home.client.place.solutions.SolutionsActivity;
import com.sap.sailing.gwt.home.client.place.solutions.TabletAndDesktopSolutionsView;
import com.sap.sailing.gwt.home.client.place.solutions.SolutionsView;
import com.sap.sailing.gwt.home.client.place.sponsoring.SponsoringActivity;
import com.sap.sailing.gwt.home.client.place.sponsoring.TabletAndDesktopSponsoringView;
import com.sap.sailing.gwt.home.client.place.sponsoring.SponsoringView;
import com.sap.sailing.gwt.home.client.place.start.SmartphoneStartView;
import com.sap.sailing.gwt.home.client.place.start.StartView;
import com.sap.sailing.gwt.ui.shared.EventDTO;
@@ -30,13 +30,11 @@ public class SmartphoneApplicationClientFactory extends AbstractApplicationClien
@Override
public EventView createEventView(EventDTO event) {
// TODO createEventView
return null;
}
@Override
public EventsView createEventsView(EventsActivity activity) {
// TODO Auto-generated method stub
return null;
}
@@ -46,14 +44,12 @@ public class SmartphoneApplicationClientFactory extends AbstractApplicationClien
}
@Override
public TabletAndDesktopSponsoringView createSponsoringView(SponsoringActivity activity) {
// TODO Auto-generated method stub
public SponsoringView createSponsoringView(SponsoringActivity activity) {
return null;
}
@Override
public TabletAndDesktopSolutionsView createSolutionsView(SolutionsActivity activity) {
// TODO Auto-generated method stub
public SolutionsView createSolutionsView(SolutionsActivity activity) {
return null;
}
}
@@ -9,8 +9,9 @@ import com.sap.sailing.gwt.home.client.place.events.EventsActivity;
import com.sap.sailing.gwt.home.client.place.events.EventsView;
import com.sap.sailing.gwt.home.client.place.events.TabletAndDesktopEventsView;
import com.sap.sailing.gwt.home.client.place.solutions.SolutionsActivity;
import com.sap.sailing.gwt.home.client.place.solutions.TabletAndDesktopSolutionsView;
import com.sap.sailing.gwt.home.client.place.solutions.SolutionsView;
import com.sap.sailing.gwt.home.client.place.sponsoring.SponsoringActivity;
import com.sap.sailing.gwt.home.client.place.sponsoring.SponsoringView;
import com.sap.sailing.gwt.home.client.place.sponsoring.TabletAndDesktopSponsoringView;
import com.sap.sailing.gwt.home.client.place.start.StartView;
import com.sap.sailing.gwt.home.client.place.start.TabletAndDesktopStartView;
@@ -46,12 +47,12 @@ public class TabletAndDesktopApplicationClientFactory extends AbstractApplicatio
}
@Override
public TabletAndDesktopSponsoringView createSponsoringView(SponsoringActivity activity) {
public SponsoringView createSponsoringView(SponsoringActivity activity) {
return new TabletAndDesktopSponsoringView();
}
@Override
public TabletAndDesktopSolutionsView createSolutionsView(SolutionsActivity activity) {
public SolutionsView createSolutionsView(SolutionsActivity activity) {
// TODO Auto-generated method stub
return null;
}
@@ -3,5 +3,5 @@ package com.sap.sailing.gwt.home.client.place.solutions;
import com.sap.sailing.gwt.ui.client.SailingClientFactory;
public interface SolutionsClientFactory extends SailingClientFactory {
TabletAndDesktopSolutionsView createSolutionsView(SolutionsActivity activity);
SolutionsView createSolutionsView(SolutionsActivity activity);
}
@@ -3,5 +3,5 @@ package com.sap.sailing.gwt.home.client.place.sponsoring;
import com.sap.sailing.gwt.ui.client.SailingClientFactory;
public interface SponsoringClientFactory extends SailingClientFactory {
TabletAndDesktopSponsoringView createSponsoringView(SponsoringActivity activity);
SponsoringView createSponsoringView(SponsoringActivity activity);
}
@@ -16,7 +16,7 @@
.eventteaser:hover .eventteaser_image {
opacity: 0.9;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)";
filter: alpha(opacity=90);
filter: literal("alpha(opacity=90)");
}
.eventteaser_image {
position: relative;
@@ -10,6 +10,7 @@ import java.util.UUID;
import com.google.gwt.user.client.rpc.RemoteService;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
@@ -56,6 +57,7 @@ import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardSearchResultDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
@@ -83,6 +85,7 @@ import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.search.KeywordQuery;
/**
* The client side stub for the RPC service. Usually, when a <code>null</code> date is passed to
@@ -480,5 +483,31 @@ public interface SailingService extends RemoteService {
Collection<String> getGPSFixImporterTypes();
List<TrackFileImportDeviceIdentifierDTO> getTrackFileImportDeviceIds(List<String> uuids)
throws NoCorrespondingServiceRegisteredException, TransformationException;
throws NoCorrespondingServiceRegisteredException, TransformationException;
/**
* A client should search a server in a two-step process. First, the client should ask the server which other
* servers are available for searching additional content. Then, in a second step, the client should fire the
* queries by parallel asynchronous calls to the one server, passing the name of the remote server reference to
* search, or <code>null</code> in order to search the server to which the query is sent by the call. This allows a
* client to asynchronously receive the results from various servers, not requiring the client to block until all
* results from all servers have been received. The key reason for this two-step process is that the GWT RPC does
* not support streaming of results.
*
* @return the list of server reference names, corresponding with {@link RemoteSailingServerReference#getName()}, to
* be used as parameter in {@link #search(String, KeywordQuery)}. This list does <em>not</em> contain the
* <code>null</code> value used to represent the search on the main server to which the query is sent.
*/
Iterable<String> getSearchServerNames();
/**
* Call this method once for each result of {@link #getSearchServerNames()} and once with <code>null</code> for
* the <code>serverNameOfNullForMain</code> parameter.
*
* @param serverNameOrNullForMain
* use <code>null</code> to search on the server to which this request is sent; use a name as retrieved
* by {@link #getSearchServerNames()} which corresponds to a name of a
* {@link RemoteSailingServerReference}, to search a remote server.
*/
Iterable<LeaderboardSearchResultDTO> search(String serverNameOrNullForMain, KeywordQuery query) throws Exception;
}
@@ -9,6 +9,7 @@ import java.util.Set;
import java.util.UUID;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
@@ -47,6 +48,7 @@ import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardSearchResultDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
@@ -73,6 +75,7 @@ import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.search.KeywordQuery;
/**
* The async counterpart of {@link SailingService}
@@ -598,4 +601,32 @@ public interface SailingServiceAsync {
void getGPSFixImporterTypes(AsyncCallback<Collection<String>> callback);
void getTrackFileImportDeviceIds(List<String> uuids, AsyncCallback<List<TrackFileImportDeviceIdentifierDTO>> callback);
/**
* A client should search a server in a two-step process. First, the client should ask the server which other
* servers are available for searching additional content. Then, in a second step, the client should fire the
* queries by parallel asynchronous calls to the one server, passing the name of the remote server reference to
* search, or <code>null</code> in order to search the server to which the query is sent by the call. This allows a
* client to asynchronously receive the results from various servers, not requiring the client to block until all
* results from all servers have been received. The key reason for this two-step process is that the GWT RPC does
* not support streaming of results.
*
* @return the list of server reference names, corresponding with {@link RemoteSailingServerReference#getName()}, to
* be used as parameter in {@link #search(String, KeywordQuery)}. This list does <em>not</em> contain the
* <code>null</code> value used to represent the search on the main server to which the query is sent.
*/
void getSearchServerNames(AsyncCallback<Iterable<String>> callback);
/**
* Call this method once for each result of {@link #getSearchServerNames()} and once with <code>null</code> for
* the <code>serverNameOfNullForMain</code> parameter.
*
* @param serverNameOrNullForMain
* use <code>null</code> to search on the server to which this request is sent; use a name as retrieved
* by {@link #getSearchServerNames()} which corresponds to a name of a
* {@link RemoteSailingServerReference}, to search a remote server.
*/
void search(String serverNameOrNullForMain, KeywordQuery query,
AsyncCallback<Iterable<LeaderboardSearchResultDTO>> callback);
}
@@ -0,0 +1,91 @@
package com.sap.sailing.gwt.ui.client.search;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.shared.LeaderboardSearchResultDTO;
import com.sap.sse.common.search.KeywordQuery;
import com.sap.sse.gwt.client.EntryPointHelper;
/**
* Sample entry point demonstrating the search capabilites of
* {@link SailingServiceAsync#search(String, com.sap.sse.common.search.KeywordQuery, AsyncCallback)} and
* {@link SailingServiceAsync#getSearchServerNames(AsyncCallback)}.
*
* @author Axel Uhl (D043530)
*
*/
public class SearchEntryPoint extends AbstractEntryPoint {
private final SailingServiceAsync sailingService = GWT.create(SailingService.class);
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath);
VerticalPanel searchAndResults = new VerticalPanel();
searchAndResults.setSize("100%", "30em");
RootPanel.get().add(searchAndResults);
HorizontalPanel searchPanel = new HorizontalPanel();
searchAndResults.add(searchPanel);
searchPanel.add(new Label("Search"));
final TextBox searchBox = new TextBox();
searchPanel.add(searchBox);
Button searchButton = new Button("Search");
searchPanel.add(searchButton);
final TextArea resultsLabel = new TextArea();
resultsLabel.setSize("90%", "70%");
resultsLabel.setEnabled(false);
searchAndResults.add(resultsLabel);
searchButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
final KeywordQuery query = new KeywordQuery(searchBox.getText().split(" "));
resultsLabel.setText("");
sailingService.getSearchServerNames(new AsyncCallback<Iterable<String>>() {
@Override
public void onSuccess(Iterable<String> serverNames) {
search(resultsLabel, query, /* null meaning the "local" server */ null);
for (String serverName : serverNames) {
search(resultsLabel, query, serverName);
}
}
@Override
public void onFailure(Throwable caught) {
Window.alert("Error: "+caught.getMessage());
}
});
}
});
}
private void search(final TextArea resultsLabel, final KeywordQuery query, final String serverName) {
sailingService.search(serverName, query, new AsyncCallback<Iterable<LeaderboardSearchResultDTO>>() {
@Override
public void onFailure(Throwable caught) {
Window.alert("Error: "+caught.getMessage());
}
@Override
public void onSuccess(Iterable<LeaderboardSearchResultDTO> result) {
for (LeaderboardSearchResultDTO r : result) {
resultsLabel.setText(resultsLabel.getText()+"\n"+r);
}
}
});
}
}
@@ -65,6 +65,8 @@ import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.RaceColumn;
@@ -270,6 +272,7 @@ import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.GateDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupBaseDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardSearchResultDTO;
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
@@ -378,6 +381,8 @@ import com.sap.sailing.server.replication.impl.ReplicaDescriptor;
import com.sap.sailing.util.BuildVersion;
import com.sap.sailing.xrr.resultimport.schema.RegattaResults;
import com.sap.sse.common.Util;
import com.sap.sse.common.search.KeywordQuery;
import com.sap.sse.common.search.Result;
/**
* The server side implementation of the RPC service.
@@ -3112,16 +3117,26 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
private EventBaseDTO convertToEventDTO(EventBase event) {
List<LeaderboardGroupBaseDTO> lgDTOs = new ArrayList<>();
if (event.getLeaderboardGroups() != null) {
for (LeaderboardGroupBase lgBase : event.getLeaderboardGroups()) {
lgDTOs.add(new LeaderboardGroupBaseDTO(lgBase.getId(), lgBase.getName(), lgBase.getDescription(), lgBase.hasOverallLeaderboard()));
final EventBaseDTO eventDTO;
if (event == null) {
eventDTO = null;
} else {
List<LeaderboardGroupBaseDTO> lgDTOs = new ArrayList<>();
if (event.getLeaderboardGroups() != null) {
for (LeaderboardGroupBase lgBase : event.getLeaderboardGroups()) {
lgDTOs.add(convertToLeaderboardGroupBaseDTO(lgBase));
}
}
eventDTO = new EventBaseDTO(event.getName(), lgDTOs);
copyEventBaseFieldToDTO(event, eventDTO);
}
EventBaseDTO eventDTO = new EventBaseDTO(event.getName(), lgDTOs);
copyEventBaseFieldToDTO(event, eventDTO);
return eventDTO;
}
private LeaderboardGroupBaseDTO convertToLeaderboardGroupBaseDTO(LeaderboardGroupBase leaderboardGroupBase) {
return new LeaderboardGroupBaseDTO(leaderboardGroupBase.getId(), leaderboardGroupBase.getName(),
leaderboardGroupBase.getDescription(), leaderboardGroupBase.hasOverallLeaderboard());
}
private void copyEventBaseFieldToDTO(EventBase event, EventBaseDTO eventDTO) {
eventDTO.venue = new VenueDTO();
@@ -4625,4 +4640,42 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
return result;
}
@Override
public Iterable<String> getSearchServerNames() {
List<String> result = new ArrayList<>();
for (RemoteSailingServerReference remoteServerRef : getService().getLiveRemoteServerReferences()) {
result.add(remoteServerRef.getName());
}
return result;
}
@Override
public Iterable<LeaderboardSearchResultDTO> search(String serverNameOrNullForMain, KeywordQuery query) throws MalformedURLException {
final List<LeaderboardSearchResultDTO> result = new ArrayList<>();
if (serverNameOrNullForMain == null) {
Result<LeaderboardSearchResult> searchResult = getService().search(query);
for (LeaderboardSearchResult hit : searchResult.getHits()) {
result.add(createLeaderboardSearchResultDTO(hit, getRequestBaseURL()));
}
} else {
RemoteSailingServerReference remoteRef = getService().getRemoteServerReferenceByName(serverNameOrNullForMain);
for (LeaderboardSearchResultBase hit : getService().searchRemotely(serverNameOrNullForMain, query).getHits()) {
result.add(createLeaderboardSearchResultDTO(hit, remoteRef.getURL()));
}
}
return result;
}
private LeaderboardSearchResultDTO createLeaderboardSearchResultDTO(LeaderboardSearchResultBase leaderboardSearchResult, URL baseURL) {
ArrayList<LeaderboardGroupBaseDTO> leaderboardGroups = new ArrayList<>();
for (LeaderboardGroupBase lgb : leaderboardSearchResult.getLeaderboardGroups()) {
LeaderboardGroupBaseDTO leaderboardGroupDTO = convertToLeaderboardGroupBaseDTO(lgb);
leaderboardGroups.add(leaderboardGroupDTO);
}
return new LeaderboardSearchResultDTO(baseURL.toString(), leaderboardSearchResult.getLeaderboard().getName(),
leaderboardSearchResult.getLeaderboard().getDisplayName(), leaderboardSearchResult.getRegattaName(),
leaderboardSearchResult.getBoatClassName(), convertToEventDTO(leaderboardSearchResult.getEvent()),
leaderboardGroups);
}
}
@@ -0,0 +1,94 @@
package com.sap.sailing.gwt.ui.shared;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
/**
* The DTO version of a {@link LeaderboardSearchResultBase} object for transmission to clients. In addition to the
* fields of a {@link LeaderboardSearchResultBase}, this object also tells the base URL of the server from which this
* result was obtained. This may be a server different from the one to which the query was sent, as such a server may
* maintain a set of {@link RemoteSailingServerReference}s pointing to other servers from where search results can be
* obtained as well. The client needs to use this base URL to navigate to a user interface / page that can display those
* search results.
*
* @author Axel Uhl (D043530)
*
*/
public class LeaderboardSearchResultDTO implements IsSerializable {
private String baseURL;
private String leaderboardName;
private String leaderboardDisplayName;
private String regattaName;
private String boatClassName;
private EventBaseDTO event;
private Iterable<LeaderboardGroupBaseDTO> leaderboardGroups;
LeaderboardSearchResultDTO() {} // for GWT RPC serialization only
public LeaderboardSearchResultDTO(String baseURL, String leaderboardName, String leaderboardDisplayName,
String regattaName, String boatClassName, EventBaseDTO event,
Iterable<LeaderboardGroupBaseDTO> leaderboardGroups) {
super();
this.baseURL = baseURL;
this.leaderboardName = leaderboardName;
this.leaderboardDisplayName = leaderboardDisplayName;
this.regattaName = regattaName;
this.boatClassName = boatClassName;
this.event = event;
this.leaderboardGroups = leaderboardGroups;
}
public String getBaseURL() {
return baseURL;
}
public String getLeaderboardName() {
return leaderboardName;
}
public String getLeaderboardDisplayName() {
return leaderboardDisplayName;
}
public String getRegattaName() {
return regattaName;
}
public String getBoatClassName() {
return boatClassName;
}
public EventBaseDTO getEvent() {
return event;
}
public Iterable<LeaderboardGroupBaseDTO> getLeaderboardGroups() {
return leaderboardGroups;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Search result from ");
sb.append(getBaseURL());
sb.append(": Leaderboard ");
sb.append(getLeaderboardName());
if (getLeaderboardDisplayName() != null) {
sb.append(" (display name: ");
sb.append(getLeaderboardDisplayName());
sb.append(")");
}
if (getEvent() != null) {
sb.append(", Event ");
sb.append(getEvent().getName());
}
sb.append(", ");
sb.append("Boat class: ");
sb.append(getBoatClassName());
sb.append("; ");
sb.append("Regatta Name: ");
sb.append(getRegattaName());
return sb.toString();
}
}
@@ -55,7 +55,7 @@
<set-property name="gwt.logging.popupHandler" value="DISABLED" />
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -28,7 +28,7 @@
<entry-point class="com.sap.sailing.gwt.ui.datamining.client.DataMiningEntryPoint" />
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -7,5 +7,5 @@
<super-source path="jre" />
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
</module>
@@ -39,7 +39,7 @@
<entry-point class='com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -35,7 +35,7 @@
<entry-point class='com.sap.sailing.gwt.ui.leaderboardedit.LeaderboardEditPage'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<source path='common'/>
<source path='client'/>
@@ -5,7 +5,7 @@
<inherits name="com.google.gwt.user.User" />
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<entry-point class="com.sap.sailing.gwt.ui.polarsheets.PolarSheetsEntryPoint">
</entry-point>
@@ -30,7 +30,7 @@
<entry-point class='com.sap.sailing.gwt.ui.raceboard.RaceBoardEntryPoint'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -46,7 +46,7 @@
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Could not determine the version of your GWT SDK; using the module DTD from GWT 1.6.4. You may want to change this. -->
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.4.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.4.0/distro-source/core/src/gwt-module.dtd">
<module>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.clean.Clean'/>
<inherits name='com.google.gwt.debug.Debug'/>
<stylesheet src="../AdminConsole.css" />
<!-- Highcharts API -->
<inherits name="org.moxieapps.gwt.highcharts.Highcharts" />
<!-- Google Maps API -->
<inherits name='com.google.gwt.ajaxloader.AjaxLoader' />
<inherits name='com.google.gwt.maps.Maps' />
<inherits name="com.google.gwt.resources.Resources" />
<inherits name="com.google.gwt.logging.Logging"/>
<!-- Other module inherits -->
<inherits name="com.sap.sailing.gwt.ui.EmulatedJRE" />
<inherits name="com.sap.sse.gwt.SSESharedGWT" />
<inherits name="com.sap.sse.SSECommon" />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<!-- Fancy DateTimePicker (without the rest of Bootstrap!) -->
<!-- GWT-Bootstrap License: Apache License, Version 2.0 -->
<inherits name="com.github.gwtbootstrap.BootstrapNoResources" />
<inherits name='com.github.gwtbootstrap.datetimepicker.DatetimepickerNoResources' />
<!-- Fancy Progressbar (without the rest of Bootstrap!) -->
<!-- GWT-Bootstrap License: Apache License, Version 2.0 -->
<stylesheet src="../AdminConsoleDateTimePicker.css" />
<stylesheet src="../AdminConsoleProgressBar.css" />
<super-source path="jre" />
<entry-point class='com.sap.sailing.gwt.ui.client.search.SearchEntryPoint'/>
<set-property name="gwt.logging.enabled" value="TRUE"/>
<set-property name="gwt.logging.logLevel" value="FINE"/>
<set-property name="gwt.logging.firebugHandler" value="ENABLED" />
<set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<set-property name="gwt.logging.consoleHandler" value="ENABLED" />
<set-property name="gwt.logging.developmentModeHandler" value="DISABLED" />
<set-property name="gwt.logging.popupHandler" value="DISABLED" />
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
<source path='client'/>
<source path='controls'/>
<source path='components'/>
<source path='adminconsole'/>
<source path='leaderboard'/>
<source path='masterdataimport'/>
<source path='usermanagement'/>
<source path='shared'/>
<source path='test'/>
<source path='actions'/>
<source path='simulator'/>
</module>
@@ -37,7 +37,7 @@
<!-- Specify the app entry point class. -->
<entry-point class='com.sap.sailing.gwt.ui.simulator.SimulatorEntryPoint'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -37,7 +37,7 @@
<entry-point class='com.sap.sailing.gwt.ui.spectator.SpectatorEntryPoint'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -32,7 +32,7 @@
<entry-point class='com.sap.sailing.gwt.ui.tv.TVEntryPoint'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="ru"/>
<source path='common'/>
@@ -30,7 +30,7 @@
<entry-point class='com.sap.sailing.gwt.ui.usermanagement.UserManagementPage'/>
<!-- Specify the paths for translatable code -->
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<source path='actions'/>
<source path='common'/>
@@ -13,7 +13,7 @@
<entry-point class="com.sap.sailing.gwt.ui.client.media.popup.VideoPopupWindow" />
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<source path='client/media/popup' />
<source path='client/shared/media' />
@@ -12,7 +12,7 @@
<entry-point class="com.sap.sailing.gwt.ui.client.media.popup.YoutubePopupWindow" />
<!-- <extend-property name="locale" values="de"/> --> <set-property name="user.agent" value="gecko1_8" />
<extend-property name="locale" values="de"/>
<source path='client/media/popup' />
<source path='client/shared/media' />
@@ -0,0 +1,53 @@
package com.sap.sailing.server.gateway.deserialization.impl;
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.impl.LeaderboardBaseImpl;
import com.sap.sailing.domain.base.impl.LeaderboardSearchResultBaseImpl;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardSearchResultJsonSerializer;
public class LeaderboardSearchResultBaseJsonDeserializer implements JsonDeserializer<LeaderboardSearchResultBase> {
private final EventBaseJsonDeserializer eventDeserializer;
private final LeaderboardGroupBaseJsonDeserializer leaderboardGroupDeserializer;
public LeaderboardSearchResultBaseJsonDeserializer(EventBaseJsonDeserializer eventDeserializer,
LeaderboardGroupBaseJsonDeserializer leaderboardGroupDeserializer) {
super();
this.eventDeserializer = eventDeserializer;
this.leaderboardGroupDeserializer = leaderboardGroupDeserializer;
}
@Override
public LeaderboardSearchResultBase deserialize(JSONObject object) throws JsonDeserializationException {
final JSONObject eventJson = (JSONObject) object.get(LeaderboardSearchResultJsonSerializer.FIELD_EVENT);
final EventBase event;
if (eventJson != null) {
event = eventDeserializer.deserialize(eventJson);
} else {
event = null;
}
JSONObject leaderboardJson = Helpers.getNestedObjectSafe(object, LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD);
String leaderboardName = (String) leaderboardJson.get(LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD_NAME);
String leaderboardDisplayName = (String) leaderboardJson.get(LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD_DISPLAY_NAME);
String boatClassName = (String) leaderboardJson.get(LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD_BOAT_CLASS_NAME);
String regattaName = (String) leaderboardJson.get(LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD_REGATTA_NAME);
JSONArray leaderboardGroupsJson = Helpers.getNestedArraySafe(leaderboardJson, LeaderboardSearchResultJsonSerializer.FIELD_LEADERBOARD_IN_LEADERBOARD_GROUPS);
List<LeaderboardGroupBase> leaderboardGroups = new ArrayList<LeaderboardGroupBase>();
for (Object leaderboardGroupJson : leaderboardGroupsJson) {
leaderboardGroups.add(leaderboardGroupDeserializer.deserialize((JSONObject) leaderboardGroupJson));
}
return new LeaderboardSearchResultBaseImpl(new LeaderboardBaseImpl(leaderboardName, leaderboardDisplayName),
regattaName, boatClassName, leaderboardGroups, event);
}
}
@@ -21,10 +21,10 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
public static final String FIELDS_LEADERBOARD_GROUPS = "leaderboardGroups";
private final JsonSerializer<Venue> venueSerializer;
private final JsonSerializer<? super LeaderboardGroupBase> leaderboardGroupSerializer;
private final JsonSerializer<? super LeaderboardGroupBase> leaderboardGroupBaseSerializer;
public EventBaseJsonSerializer(JsonSerializer<Venue> venueSerializer, JsonSerializer<? super LeaderboardGroupBase> leaderboardGroupSerializer) {
this.leaderboardGroupSerializer = leaderboardGroupSerializer;
public EventBaseJsonSerializer(JsonSerializer<Venue> venueSerializer, JsonSerializer<? super LeaderboardGroupBase> leaderboardGroupBaseSerializer) {
this.leaderboardGroupBaseSerializer = leaderboardGroupBaseSerializer;
this.venueSerializer = venueSerializer;
}
@@ -40,7 +40,7 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
JSONArray leaderboardGroups = new JSONArray();
result.put(FIELDS_LEADERBOARD_GROUPS, leaderboardGroups);
for (LeaderboardGroupBase lg : event.getLeaderboardGroups()) {
leaderboardGroups.add(leaderboardGroupSerializer.serialize(lg));
leaderboardGroups.add(leaderboardGroupBaseSerializer.serialize(lg));
}
return result;
}
@@ -0,0 +1,63 @@
package com.sap.sailing.server.gateway.serialization.impl;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class LeaderboardSearchResultJsonSerializer implements JsonSerializer<LeaderboardSearchResult> {
public static final String FIELD_SERVER_BASE_URL = "serverBaseURL";
public static final String FIELD_EVENT = "event";
public static final String FIELD_LEADERBOARD = "leaderboard";
public static final String FIELD_LEADERBOARD_NAME = "name";
public static final String FIELD_LEADERBOARD_DISPLAY_NAME = "displayName";
public static final String FIELD_LEADERBOARD_BOAT_CLASS_NAME = "boatClassName";
public static final String FIELD_LEADERBOARD_REGATTA_NAME = "regattaName";
public static final String FIELD_LEADERBOARD_IN_LEADERBOARD_GROUPS = "inLeaderboardGroups";
private final JsonSerializer<EventBase> eventBaseJsonSerializer;
private final JsonSerializer<LeaderboardGroupBase> leaderboardGroupBaseJsonSerializer;
public LeaderboardSearchResultJsonSerializer(JsonSerializer<EventBase> eventBaseJsonSerializer, JsonSerializer<LeaderboardGroupBase> leaderboardGroupBaseJsonSerializer) {
this.eventBaseJsonSerializer = eventBaseJsonSerializer;
this.leaderboardGroupBaseJsonSerializer = leaderboardGroupBaseJsonSerializer;
}
@Override
public JSONObject serialize(LeaderboardSearchResult leaderboardSearchResult) {
JSONObject result = new JSONObject();
result.put(
FIELD_EVENT,
leaderboardSearchResult.getEvent() == null ? null : eventBaseJsonSerializer
.serialize(leaderboardSearchResult.getEvent()));
JSONObject leaderboardJson = new JSONObject();
result.put(FIELD_LEADERBOARD, leaderboardJson);
Leaderboard leaderboard = leaderboardSearchResult.getLeaderboard();
leaderboardJson.put(FIELD_LEADERBOARD_NAME, leaderboard.getName());
leaderboardJson.put(FIELD_LEADERBOARD_DISPLAY_NAME, leaderboard.getDisplayName());
final String boatClassName;
final Regatta regatta;
if (leaderboard instanceof RegattaLeaderboard) {
regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
boatClassName = regatta.getBoatClass().getName();
} else {
regatta = null;
boatClassName = leaderboardSearchResult.getBoatClassName();
}
leaderboardJson.put(FIELD_LEADERBOARD_BOAT_CLASS_NAME, boatClassName);
leaderboardJson.put(FIELD_LEADERBOARD_REGATTA_NAME, regatta == null ? null : regatta.getName());
JSONArray leaderboardGroupsJson = new JSONArray();
leaderboardJson.put(FIELD_LEADERBOARD_IN_LEADERBOARD_GROUPS, leaderboardGroupsJson);
for (LeaderboardGroup lg : leaderboardSearchResult.getLeaderboardGroups()) {
leaderboardGroupsJson.add(leaderboardGroupBaseJsonSerializer.serialize(lg));
}
return result;
}
}
@@ -13,8 +13,7 @@ public class RestApiApplication extends Application {
classes.add(EventsResource.class);
classes.add(RegattasResource.class);
classes.add(LeaderboardsResource.class);
// classes.add(WindResource.class);
classes.add(SearchResource.class);
return classes;
}
}
@@ -0,0 +1,48 @@
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.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONArray;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardGroupBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardSearchResultJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.VenueJsonSerializer;
import com.sap.sse.common.search.KeywordQuery;
import com.sap.sse.common.search.Result;
@Path("/v1/search")
public class SearchResource extends AbstractSailingServerResource {
private final JsonSerializer<LeaderboardSearchResult> serializer;
public SearchResource() {
final LeaderboardGroupBaseJsonSerializer leaderboardGroupSerializer = new LeaderboardGroupBaseJsonSerializer();
serializer = new LeaderboardSearchResultJsonSerializer(new EventBaseJsonSerializer(new VenueJsonSerializer(
new CourseAreaJsonSerializer()), leaderboardGroupSerializer), leaderboardGroupSerializer);
}
private Result<LeaderboardSearchResult> search(KeywordQuery query) {
return getService().search(query);
}
@GET
@Produces("application/json;charset=UTF-8")
public Response search(@QueryParam("q") String keywords) {
KeywordQuery query = new KeywordQuery(keywords.split(" "));
Iterable<LeaderboardSearchResult> searchResults = search(query).getHits();
JSONArray jsonSearchResults = new JSONArray();
for (LeaderboardSearchResult searchResult : searchResults) {
jsonSearchResults.add(serializer.serialize(searchResult));
}
return Response.ok(jsonSearchResults.toJSONString(), MediaType.APPLICATION_JSON).build();
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.server.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.net.URL;
import java.util.ArrayList;
@@ -21,7 +22,11 @@ import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.base.Waypoint;
@@ -36,14 +41,25 @@ import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.test.AbstractTracTracLiveTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.server.LeaderboardSearchResult;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardSearchResultBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardGroupBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardSearchResultJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.VenueJsonSerializer;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.server.operationaltransformation.AddColumnToSeries;
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
@@ -241,10 +257,14 @@ public class SearchServiceTest {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Kiel" })));
assertEquals(2, Util.size(searchResults.getHits()));
final Iterator<LeaderboardSearchResult> iterator = searchResults.getHits().iterator();
Regatta firstFoundRegatta = iterator.next().getRegatta();
final LeaderboardSearchResult firstMatch = iterator.next();
Regatta firstFoundRegatta = firstMatch.getRegatta();
assertSame(pfingstbusch29er, firstFoundRegatta);
Regatta secondFoundRegatta = iterator.next().getRegatta();
assertSame(pfingstbusch, firstMatch.getEvent());
final LeaderboardSearchResult secondMatch = iterator.next();
Regatta secondFoundRegatta = secondMatch.getRegatta();
assertSame(pfingstbusch470, secondFoundRegatta);
assertSame(pfingstbusch, secondMatch.getEvent());
}
@Test
@@ -252,9 +272,44 @@ public class SearchServiceTest {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" })));
assertEquals(2, Util.size(searchResults.getHits()));
final Iterator<LeaderboardSearchResult> iter = searchResults.getHits().iterator();
Regatta earlierStartRegatta = iter.next().getRegatta();
final LeaderboardSearchResult firstMatch = iter.next();
Regatta earlierStartRegatta = firstMatch.getRegatta();
assertSame(pfingstbusch470, earlierStartRegatta);
Regatta laterStartRegatta = iter.next().getRegatta();
assertSame(pfingstbusch, firstMatch.getEvent());
final LeaderboardSearchResult secondMatch = iter.next();
Regatta laterStartRegatta = secondMatch.getRegatta();
assertSame(aalRegatta, laterStartRegatta);
assertSame(aalEvent, secondMatch.getEvent());
}
@Test
public void testSerializeAndDeserializeSearchResult() throws JsonDeserializationException {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" })));
LeaderboardGroupBaseJsonSerializer leaderboardGroupBaseJsonSerializer = new LeaderboardGroupBaseJsonSerializer();
LeaderboardSearchResultJsonSerializer serializer = new LeaderboardSearchResultJsonSerializer(new EventBaseJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()),
leaderboardGroupBaseJsonSerializer),
leaderboardGroupBaseJsonSerializer);
LeaderboardGroupBaseJsonDeserializer leaderboardGroupBaseJsonDeserializer = new LeaderboardGroupBaseJsonDeserializer();
LeaderboardSearchResultBaseJsonDeserializer deserializer = new LeaderboardSearchResultBaseJsonDeserializer(
new EventBaseJsonDeserializer(new VenueJsonDeserializer(new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)),
leaderboardGroupBaseJsonDeserializer), leaderboardGroupBaseJsonDeserializer);
final LeaderboardSearchResult expected = searchResults.getHits().iterator().next();
LeaderboardSearchResultBase deserialized = deserializer.deserialize(serializer.serialize(expected));
assertEquals("Pfingstbusch (470)", deserialized.getRegattaName());
assertEquals(expected.getEvent().getName(), deserialized.getEvent().getName());
assertEquals(expected.getEvent().getId(), deserialized.getEvent().getId());
assertEquals(expected.getEvent().getStartDate(), deserialized.getEvent().getStartDate());
assertEquals(expected.getEvent().getEndDate(), deserialized.getEvent().getEndDate());
assertEquals(expected.getEvent().getVenue().getName(), deserialized.getEvent().getVenue().getName());
Iterator<LeaderboardGroup> expectedLGs = expected.getLeaderboardGroups().iterator();
Iterator<? extends LeaderboardGroupBase> deserializedLGs = deserialized.getLeaderboardGroups().iterator();
while (expectedLGs.hasNext()) {
assertTrue(deserializedLGs.hasNext());
LeaderboardGroup expectedLG = expectedLGs.next();
LeaderboardGroupBase deserializedLG = deserializedLGs.next();
assertEquals(expectedLG.getId(), deserializedLG.getId());
assertEquals(expectedLG.getName(), deserializedLG.getName());
assertEquals(expectedLG.getDescription(), deserializedLG.getDescription());
}
}
}
@@ -62,8 +62,12 @@ public abstract class BaseRouteConverterGPSFixImporterImpl extends BaseGPSFixImp
Method m = NavigationFormatParser.class.getDeclaredMethod("read", InputStream.class, Integer.TYPE,
CompactCalendar.class, List.class);
m.setAccessible(true);
ParserResult result = parser.read(inputStream, /* read buffer size; 1GB max size as
RouteConverter currently buffers all streams, also the successful ones... */ 1024 * 1024 * 1024, null, supportedReadFormats);
ParserResult result = parser.read(inputStream, /* read buffer size; 1GB max... the problem is that some
* formats continue to read to the end even if they don't
* happen to find anything in the stream because they assume
* that something parsable may be coming late in the
* stream...
*/ 1024 * 1024 * 1024, null, supportedReadFormats);
if (result == null) {
throw new FormatNotSupportedException();
}
@@ -1,10 +0,0 @@
package com.sap.sailing.server;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sse.common.search.Hit;
public interface LeaderboardSearchResult extends Hit {
Regatta getRegatta();
Leaderboard getLeaderboard();
}
@@ -21,6 +21,8 @@ import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
@@ -555,5 +557,25 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
*/
Util.Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref);
/**
* Searches the content of this server, not that of any remote servers referenced by any {@link RemoteSailingServerReference}s.
*/
Result<LeaderboardSearchResult> search(KeywordQuery query);
/**
* Searches a specific remote server whose reference has the {@link RemoteSailingServerReference#getName() name}
* <code>remoteServerReferenceName</code>. If a remote server reference with that name is not known,
* <code>null</code> is returned. Otherwise, a non-<code>null</code> and possibly empty search result set is
* returned.
*/
Result<LeaderboardSearchResultBase> searchRemotely(String remoteServerReferenceName, KeywordQuery query);
/**
* References to remote servers may be dead or alive. This is internally determined by regularly polling those
* servers for their events list. If the events list cannot be successfully retrieved, the server is considered "dead."
* This method returns the "live" server references.
*/
Iterable<RemoteSailingServerReference> getLiveRemoteServerReferences();
RemoteSailingServerReference getRemoteServerReferenceByName(String remoteServerReferenceName);
}
@@ -5,28 +5,18 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.LeaderboardSearchResult;
import com.sap.sse.common.Util;
public class RegattaSearchResultRanker implements Comparator<LeaderboardSearchResult> {
private final TrackedRegattaRegistry trackedRegattaRegistry;
protected RegattaSearchResultRanker(RacingEventService racingEventService) {
this.trackedRegattaRegistry = racingEventService;
}
public class LeaderboardSearchResultBaseRanker<T extends LeaderboardSearchResultBase> implements Comparator<T> {
@Override
public int compare(LeaderboardSearchResult o1, LeaderboardSearchResult o2) {
Regatta r1 = o1.getRegatta();
Regatta r2 = o2.getRegatta();
TrackedRegatta trackedR1 = r1 == null ? null : trackedRegattaRegistry.getTrackedRegatta(r1);
TrackedRegatta trackedR2 = r2 == null ? null : trackedRegattaRegistry.getTrackedRegatta(r2);
public int compare(T o1, T o2) {
TrackedRegatta trackedR1 = getTrackedRegatta(o1);
TrackedRegatta trackedR2 = getTrackedRegatta(o2);
final int result;
if (trackedR1 == null) {
if (trackedR2 == null) {
@@ -44,16 +34,8 @@ public class RegattaSearchResultRanker implements Comparator<LeaderboardSearchRe
}
return result;
}
private int compareByEarliestStartOfATrackedRace(TrackedRegatta trackedR1, TrackedRegatta trackedR2) {
assert trackedR1 != null;
assert trackedR2 != null;
TimePoint earliestTrackedRaceInR1 = getEarliestTrackedRace(trackedR1);
TimePoint earliestTrackedRaceInR2 = getEarliestTrackedRace(trackedR2);
return Util.compareToWithNull(earliestTrackedRaceInR1, earliestTrackedRaceInR2, /* nullIsLess */ false);
}
private TimePoint getEarliestTrackedRace(TrackedRegatta trackedRegatta) {
protected TimePoint getEarliestTrackedRace(TrackedRegatta trackedRegatta) {
List<TimePoint> startOfTrackingTimes = new ArrayList<>();
for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) {
if (trackedRace.getStartOfTracking() != null) {
@@ -68,4 +50,20 @@ public class RegattaSearchResultRanker implements Comparator<LeaderboardSearchRe
}
return result;
}
protected int compareByEarliestStartOfATrackedRace(TrackedRegatta trackedR1, TrackedRegatta trackedR2) {
assert trackedR1 != null;
assert trackedR2 != null;
TimePoint earliestTrackedRaceInR1 = getEarliestTrackedRace(trackedR1);
TimePoint earliestTrackedRaceInR2 = getEarliestTrackedRace(trackedR2);
return Util.compareToWithNull(earliestTrackedRaceInR1, earliestTrackedRaceInR2, /* nullIsLess */ false);
}
/**
* Can't know a tracked regatta if we only have a {@link LeaderboardSearchResultBase} which doesn't reference a
* real {@link Regatta} object.
*/
protected TrackedRegatta getTrackedRegatta(T o1) {
return null;
}
}
@@ -1,29 +0,0 @@
package com.sap.sailing.server.impl;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.server.LeaderboardSearchResult;
public class LeaderboardSearchResultImpl implements LeaderboardSearchResult {
private final Leaderboard leaderboard;
public LeaderboardSearchResultImpl(Leaderboard leaderboard) {
this.leaderboard = leaderboard;
}
public Regatta getRegatta() {
final Regatta regatta;
if (leaderboard instanceof RegattaLeaderboard) {
regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
} else {
regatta = null;
}
return regatta;
}
@Override
public Leaderboard getLeaderboard() {
return leaderboard;
}
}
@@ -0,0 +1,21 @@
package com.sap.sailing.server.impl;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.server.RacingEventService;
public class LeaderboardSearchResultRanker extends LeaderboardSearchResultBaseRanker<LeaderboardSearchResult> {
private final TrackedRegattaRegistry trackedRegattaRegistry;
protected LeaderboardSearchResultRanker(RacingEventService racingEventService) {
this.trackedRegattaRegistry = racingEventService;
}
protected TrackedRegatta getTrackedRegatta(LeaderboardSearchResult o1) {
Regatta r1 = o1.getRegatta();
TrackedRegatta trackedR1 = r1 == null ? null : trackedRegattaRegistry.getTrackedRegatta(r1);
return trackedR1;
}
}
@@ -1,12 +1,16 @@
package com.sap.sailing.server.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -28,6 +32,10 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
@@ -39,6 +47,8 @@ import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
@@ -133,8 +143,12 @@ import com.sap.sailing.operationaltransformation.Operation;
import com.sap.sailing.server.OperationExecutionListener;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
import com.sap.sailing.server.LeaderboardSearchResult;
import com.sap.sailing.server.Replicator;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardSearchResultBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
import com.sap.sailing.server.masterdata.DataImportLockWithProgress;
import com.sap.sailing.server.operationaltransformation.AddCourseArea;
import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta;
@@ -174,6 +188,7 @@ import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
import com.sap.sse.common.Util;
import com.sap.sse.common.search.KeywordQuery;
import com.sap.sse.common.search.Result;
import com.sap.sse.common.search.ResultImpl;
public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport, RegattaListener, LeaderboardRegistry, Replicator {
private static final Logger logger = Logger.getLogger(RacingEventServiceImpl.class.getName());
@@ -881,6 +896,16 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
mongoObjectFactory.storeSailingServer(result);
return result;
}
@Override
public Iterable<RemoteSailingServerReference> getLiveRemoteServerReferences() {
return remoteSailingServerSet.getLiveRemoteServerReferences();
}
@Override
public RemoteSailingServerReference getRemoteServerReferenceByName(String remoteServerReferenceName) {
return remoteSailingServerSet.getServerReferenceByName(remoteServerReferenceName);
}
@Override
public com.sap.sse.common.Util.Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) {
@@ -2594,6 +2619,53 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
@Override
public Result<LeaderboardSearchResult> search(KeywordQuery query) {
return new RegattaByKeywordSearchService().search(this, query);
long start = System.currentTimeMillis();
logger.info("Searching local server for "+query);
Result<LeaderboardSearchResult> result = new RegattaByKeywordSearchService().search(this, query);
logger.fine("Search for "+query+" took "+(System.currentTimeMillis()-start)+"ms");
return result;
}
@Override
public Result<LeaderboardSearchResultBase> searchRemotely(String remoteServerReferenceName, KeywordQuery query) {
long start = System.currentTimeMillis();
ResultImpl<LeaderboardSearchResultBase> result = null;
RemoteSailingServerReference remoteRef = remoteSailingServerSet.getServerReferenceByName(remoteServerReferenceName);
if (remoteRef == null) {
result = null;
} else {
BufferedReader bufferedReader = null;
try {
try {
final URL eventsURL = new URL(remoteRef.getURL(), "sailingserver/api/v1/search?q="+URLEncoder.encode(query.toString(), "UTF-8"));
logger.info("Searching remote server "+remoteRef+" for "+query);
URLConnection urlConnection = eventsURL.openConnection();
urlConnection.connect();
bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
JSONParser parser = new JSONParser();
Object eventsAsObject = parser.parse(bufferedReader);
final LeaderboardGroupBaseJsonDeserializer leaderboardGroupBaseJsonDeserializer = new LeaderboardGroupBaseJsonDeserializer();
LeaderboardSearchResultBaseJsonDeserializer deserializer = new LeaderboardSearchResultBaseJsonDeserializer(
new EventBaseJsonDeserializer(new VenueJsonDeserializer(new CourseAreaJsonDeserializer(
DomainFactory.INSTANCE)), leaderboardGroupBaseJsonDeserializer),
leaderboardGroupBaseJsonDeserializer);
result = new ResultImpl<LeaderboardSearchResultBase>(query, new LeaderboardSearchResultBaseRanker<LeaderboardSearchResultBase>());
JSONArray hitsAsJsonArray = (JSONArray) eventsAsObject;
for (Object hitAsObject : hitsAsJsonArray) {
JSONObject hitAsJson = (JSONObject) hitAsObject;
LeaderboardSearchResultBase hit = deserializer.deserialize(hitAsJson);
result.addHit(hit);
}
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
}
} catch (IOException | ParseException e) {
logger.log(Level.INFO, "Exception trying to fetch events from remote server "+remoteRef+": "+e.getMessage(), e);
}
}
logger.fine("Remote search on "+remoteRef+" for "+query+" took "+(System.currentTimeMillis()-start)+"ms");
return result;
}
}
@@ -1,15 +1,19 @@
package com.sap.sailing.server.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.impl.LeaderboardSearchResultImpl;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.server.LeaderboardSearchResult;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.common.Util;
import com.sap.sse.common.filter.AbstractListFilter;
@@ -29,10 +33,13 @@ import com.sap.sse.common.search.ResultImpl;
*/
public class RegattaByKeywordSearchService {
Result<LeaderboardSearchResult> search(final RacingEventService racingEventService, KeywordQuery query) {
ResultImpl<LeaderboardSearchResult> result = new ResultImpl<>(query, new RegattaSearchResultRanker(racingEventService));
ResultImpl<LeaderboardSearchResult> result = new ResultImpl<>(query, new LeaderboardSearchResultRanker(racingEventService));
final Map<Leaderboard, Event> eventForLeaderboard = new HashMap<>();
final Map<Leaderboard, Set<LeaderboardGroup>> leaderboardGroupsForLeaderboard = new HashMap<>();
AbstractListFilter<Leaderboard> leaderboardFilter = new AbstractListFilter<Leaderboard>() {
@Override
public Iterable<String> getStrings(Leaderboard leaderboard) {
// TODO allow recording which part of the leaderboard was matched by the keywords by returning "annotated strings" that the matcher can understand
List<String> leaderboardStrings = new ArrayList<>();
leaderboardStrings.add(leaderboard.getName());
leaderboardStrings.add(leaderboard.getDisplayName());
@@ -49,16 +56,23 @@ public class RegattaByKeywordSearchService {
for (LeaderboardGroup leaderboardGroup : leaderboardGroupsHostingLeaderboard) {
leaderboardStrings.add(leaderboardGroup.getName());
leaderboardStrings.add(leaderboardGroup.getDescription());
Set<LeaderboardGroup> leaderboardGroups = leaderboardGroupsForLeaderboard.get(leaderboard);
if (leaderboardGroups == null) {
leaderboardGroups = new HashSet<>();
leaderboardGroupsForLeaderboard.put(leaderboard, leaderboardGroups);
}
leaderboardGroups.add(leaderboardGroup);
}
for (Event event : getEventsHostingLeaderboard(leaderboard, racingEventService, leaderboardGroupsHostingLeaderboard)) {
leaderboardStrings.add(event.getName());
leaderboardStrings.add(event.getVenue().getName());
eventForLeaderboard.put(leaderboard, event);
}
return leaderboardStrings;
}
};
for (Leaderboard matchingLeaderboard : leaderboardFilter.applyFilter(query.getKeywords(), racingEventService.getLeaderboards().values())) {
result.addHit(new LeaderboardSearchResultImpl(matchingLeaderboard));
result.addHit(new LeaderboardSearchResultImpl(matchingLeaderboard, eventForLeaderboard.get(matchingLeaderboard), leaderboardGroupsForLeaderboard.get(matchingLeaderboard)));
}
return result;
}
@@ -6,8 +6,10 @@ import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -22,7 +24,6 @@ import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
@@ -30,6 +31,7 @@ import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserial
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
/**
* A set of {@link RemoteSailingServerReference}s including a cache of their {@link EventBase events} that is
@@ -44,7 +46,8 @@ public class RemoteSailingServerSet {
private static final Logger logger = Logger.getLogger(RemoteSailingServerSet.class.getName());
/**
* Holds the {@link Event} objects for the events of all registered sailing server instances.
* Holds the remote server references managed by this set. Keys are the
* {@link RemoteSailingServerReference#getName() names} of the server references.
*/
private final ConcurrentHashMap<String, RemoteSailingServerReference> remoteSailingServers;
@@ -76,6 +79,14 @@ public class RemoteSailingServerSet {
triggerAsynchronousEventCacheUpdate(ref);
}
}
/**
* If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name} equals
* <code>name</code>, it is returned. Otherwise, <code>null</code> is returned.
*/
public RemoteSailingServerReference getServerReferenceByName(String name) {
return remoteSailingServers.get(name);
}
private void triggerAsynchronousEventCacheUpdate(final RemoteSailingServerReference ref) {
new Thread("Event Cache Updater for remote server "+ref) {
@@ -150,4 +161,15 @@ public class RemoteSailingServerSet {
}
return updateRemoteServerEventCacheSynchronously(ref);
}
public Iterable<RemoteSailingServerReference> getLiveRemoteServerReferences() {
List<RemoteSailingServerReference> result = new ArrayList<>();
for (Map.Entry<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> e : cachedEventsForRemoteSailingServers.entrySet()) {
if (e.getValue().getB() == null) {
// no exception; reference considered live
result.add(e.getKey());
}
}
return result;
}
}
@@ -1,20 +1,52 @@
package com.sap.sse.common.search;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class KeywordQuery implements Query {
private final Iterable<String> keywords;
import com.sap.sse.common.Util;
public class KeywordQuery implements Query, Serializable {
private static final long serialVersionUID = -4214703751947494064L;
private Iterable<String> keywords;
KeywordQuery() {} // for GWT RPC serialization
public KeywordQuery(String... keywords) {
this.keywords = Arrays.asList(keywords);
this.keywords = trim(Arrays.asList(keywords));
}
private Iterable<String> trim(Iterable<String> keywords) {
final List<String> result = new ArrayList<String>(Util.size(keywords));
for (String keyword : keywords) {
result.add(keyword.trim());
}
return result;
}
public KeywordQuery(Iterable<String> keywords) {
super();
this.keywords = keywords;
this.keywords = trim(keywords);
}
public Iterable<String> getKeywords() {
return keywords;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String keyword : keywords) {
if (first) {
first = false;
} else {
sb.append(' ');
}
sb.append(keyword);
}
return sb.toString();
}
}
@@ -17,7 +17,6 @@ java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# Overwrite the log leves for specific loggers here
com.sap.sailing.level = FINE
com.sap.sailing.domain.tracking.impl.level = FINEST
org.eclipse.jetty.level = INFO
org.apache.activemq.level = INFO