diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch
index 5d943eeb607..e6afbfc6ff3 100755
--- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch
+++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch
@@ -7,7 +7,7 @@
-
+
@@ -53,7 +53,7 @@
-
+
diff --git a/java/com.sap.sailing.gwt.ui/Search.html b/java/com.sap.sailing.gwt.ui/Search.html
new file mode 100755
index 00000000000..02da77912e7
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/Search.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SAP Sailing Analytics Search Demonstration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
index b89a2d73a75..ecb6c42d0b6 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
@@ -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 null date is passed to
@@ -480,5 +483,31 @@ public interface SailingService extends RemoteService {
Collection getGPSFixImporterTypes();
List getTrackFileImportDeviceIds(List 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 null 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 not contain the
+ * null value used to represent the search on the main server to which the query is sent.
+ */
+ Iterable getSearchServerNames();
+
+ /**
+ * Call this method once for each result of {@link #getSearchServerNames()} and once with null for
+ * the serverNameOfNullForMain parameter.
+ *
+ * @param serverNameOrNullForMain
+ * use null 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 search(String serverNameOrNullForMain, KeywordQuery query) throws Exception;
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
index b54e1093b16..3e13a5023b3 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
@@ -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> callback);
void getTrackFileImportDeviceIds(List uuids, AsyncCallback> 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 null 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 not contain the
+ * null value used to represent the search on the main server to which the query is sent.
+ */
+ void getSearchServerNames(AsyncCallback> callback);
+
+
+ /**
+ * Call this method once for each result of {@link #getSearchServerNames()} and once with null for
+ * the serverNameOfNullForMain parameter.
+ *
+ * @param serverNameOrNullForMain
+ * use null 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> callback);
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/search/SearchEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/search/SearchEntryPoint.java
new file mode 100755
index 00000000000..6c300d20805
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/search/SearchEntryPoint.java
@@ -0,0 +1,88 @@
+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;
+
+/**
+ * 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();
+ registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath);
+ VerticalPanel searchAndResults = new VerticalPanel();
+ 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.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>() {
+ @Override
+ public void onSuccess(Iterable 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>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Window.alert("Error: "+caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(Iterable result) {
+ for (LeaderboardSearchResultDTO r : result) {
+ resultsLabel.setText(resultsLabel.getText()+"\n"+r);
+ }
+ }
+ });
+ }
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
index 52a2f84eaf7..cc84e749d99 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
@@ -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 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 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 getSearchServerNames() {
+ List result = new ArrayList<>();
+ for (RemoteSailingServerReference remoteServerRef : getService().getLiveRemoteServerReferences()) {
+ result.add(remoteServerRef.getName());
+ }
+ return result;
+ }
+
+ @Override
+ public Iterable search(String serverNameOrNullForMain, KeywordQuery query) throws MalformedURLException {
+ final List result = new ArrayList<>();
+ if (serverNameOrNullForMain == null) {
+ Result 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 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);
+ }
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Search.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Search.gwt.xml
new file mode 100755
index 00000000000..177244e8f24
--- /dev/null
+++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Search.gwt.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java
index 62d95784be6..a24a0a86440 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java
@@ -22,6 +22,7 @@ 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;
@@ -556,5 +557,25 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
*/
Util.Pair, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref);
+ /**
+ * Searches the content of this server, not that of any remote servers referenced by any {@link RemoteSailingServerReference}s.
+ */
Result search(KeywordQuery query);
+
+ /**
+ * Searches a specific remote server whose reference has the {@link RemoteSailingServerReference#getName() name}
+ * remoteServerReferenceName. If a remote server reference with that name is not known,
+ * null is returned. Otherwise, a non-null and possibly empty search result set is
+ * returned.
+ */
+ Result 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 getLiveRemoteServerReferences();
+
+ RemoteSailingServerReference getRemoteServerReferenceByName(String remoteServerReferenceName);
}
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaSearchResultRanker.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java
similarity index 66%
rename from java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaSearchResultRanker.java
rename to java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java
index 1b0ea9adaad..a942f6d21a2 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaSearchResultRanker.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java
@@ -5,28 +5,18 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-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.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.sse.common.Util;
-public class RegattaSearchResultRanker implements Comparator {
- private final TrackedRegattaRegistry trackedRegattaRegistry;
-
- protected RegattaSearchResultRanker(RacingEventService racingEventService) {
- this.trackedRegattaRegistry = racingEventService;
- }
-
+public class LeaderboardSearchResultBaseRanker implements Comparator {
@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 startOfTrackingTimes = new ArrayList<>();
for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) {
if (trackedRace.getStartOfTracking() != null) {
@@ -68,4 +50,20 @@ public class RegattaSearchResultRanker implements Comparator {
+ 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;
+ }
+}
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
index c695970aa69..99b138d8de8 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
@@ -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;
@@ -40,6 +48,7 @@ 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;
@@ -135,6 +144,11 @@ import com.sap.sailing.server.OperationExecutionListener;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
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 getLiveRemoteServerReferences() {
+ return remoteSailingServerSet.getLiveRemoteServerReferences();
+ }
+
+ @Override
+ public RemoteSailingServerReference getRemoteServerReferenceByName(String remoteServerReferenceName) {
+ return remoteSailingServerSet.getServerReferenceByName(remoteServerReferenceName);
+ }
@Override
public com.sap.sse.common.Util.Pair, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) {
@@ -2594,6 +2619,53 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
@Override
public Result search(KeywordQuery query) {
- return new RegattaByKeywordSearchService().search(this, query);
+ long start = System.currentTimeMillis();
+ logger.info("Searching local server for "+query);
+ Result result = new RegattaByKeywordSearchService().search(this, query);
+ logger.fine("Search for "+query+" took "+(System.currentTimeMillis()-start)+"ms");
+ return result;
+ }
+
+ @Override
+ public Result searchRemotely(String remoteServerReferenceName, KeywordQuery query) {
+ long start = System.currentTimeMillis();
+ ResultImpl 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(query, new LeaderboardSearchResultBaseRanker());
+ 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;
}
}
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java
index a2fdd73974f..72aa4fce917 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java
@@ -33,7 +33,7 @@ import com.sap.sse.common.search.ResultImpl;
*/
public class RegattaByKeywordSearchService {
Result search(final RacingEventService racingEventService, KeywordQuery query) {
- ResultImpl result = new ResultImpl<>(query, new RegattaSearchResultRanker(racingEventService));
+ ResultImpl result = new ResultImpl<>(query, new LeaderboardSearchResultRanker(racingEventService));
final Map eventForLeaderboard = new HashMap<>();
final Map> leaderboardGroupsForLeaderboard = new HashMap<>();
AbstractListFilter leaderboardFilter = new AbstractListFilter() {
diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java
index cae67699a95..1d23a8ceaca 100755
--- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java
+++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RemoteSailingServerSet.java
@@ -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 remoteSailingServers;
@@ -76,6 +79,14 @@ public class RemoteSailingServerSet {
triggerAsynchronousEventCacheUpdate(ref);
}
}
+
+ /**
+ * If this set has a {@link RemoteSailingServerReference} whose {@link RemoteSailingServerReference#getName() name} equals
+ * name, it is returned. Otherwise, null 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 getLiveRemoteServerReferences() {
+ List result = new ArrayList<>();
+ for (Map.Entry, Exception>> e : cachedEventsForRemoteSailingServers.entrySet()) {
+ if (e.getValue().getB() == null) {
+ // no exception; reference considered live
+ result.add(e.getKey());
+ }
+ }
+ return result;
+ }
}
diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/search/KeywordQuery.java b/java/com.sap.sse.common/src/com/sap/sse/common/search/KeywordQuery.java
index ef1e064979c..dc6a5a49d3c 100755
--- a/java/com.sap.sse.common/src/com/sap/sse/common/search/KeywordQuery.java
+++ b/java/com.sap.sse.common/src/com/sap/sse/common/search/KeywordQuery.java
@@ -1,14 +1,19 @@
package com.sap.sse.common.search;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.sap.sse.common.Util;
-public class KeywordQuery implements Query {
- private final Iterable keywords;
+public class KeywordQuery implements Query, Serializable {
+ private static final long serialVersionUID = -4214703751947494064L;
+
+ private Iterable keywords;
+ KeywordQuery() {} // for GWT RPC serialization
+
public KeywordQuery(String... keywords) {
this.keywords = trim(Arrays.asList(keywords));
}
@@ -32,6 +37,16 @@ public class KeywordQuery implements Query {
@Override
public String toString() {
- return ""+keywords;
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String keyword : keywords) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(' ');
+ }
+ sb.append(keyword);
+ }
+ return sb.toString();
}
}