From 8428ea160b39aff4ffb971728dc637c475a720c1 Mon Sep 17 00:00:00 2001 From: Georg Herdt Date: Fri, 20 Nov 2020 17:17:19 +0100 Subject: [PATCH] switch to post bug 5410 --- .../jaxrs/api/TrackedRaceListResource.java | 17 +++++ .../server/impl/RemoteSailingServerSet.java | 65 ++++++++++++++----- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackedRaceListResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackedRaceListResource.java index ef40af1f946..a23568bfd8f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackedRaceListResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackedRaceListResource.java @@ -13,7 +13,9 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import javax.ws.rs.DefaultValue; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; @@ -59,6 +61,21 @@ public class TrackedRaceListResource extends AbstractSailingServerResource { return getJsonResponse(streamingOutput(detailedRaceListJsonSerializer.serialize(detailedRaceInfo))); } + /** + *

POST variant of the {@link #raceList(Boolean, String, String)} interface. Just uses HTTP post to circumvent HTTP 418 + * issues with large number of events.

+ *

Parameters must be provided within a form body.

+ * @see {@link #raceList(Boolean, String, String)} + */ + @POST + @Produces(CONTENT_TYPE_JSON_UTF8) + @Path("getRaces") + public Response raceListPost(@FormParam("transitive") @DefaultValue("false") Boolean transitive, + @FormParam("events") @DefaultValue("") String strEvents, + @FormParam("pred") @DefaultValue("excl") String predicate) { + return raceList(transitive, strEvents, predicate); + } + /** * Returns a list of tracked races. By default, only TrackedRaces from the local instance are returned. The entries * are grouped by the remote URL from where they originated. Local entries have a {@code null} value for the 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 f4fbd4945b0..d490e23fa65 100644 --- 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 @@ -3,7 +3,9 @@ package com.sap.sailing.server.impl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -15,6 +17,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -48,6 +51,7 @@ import com.sap.sailing.server.gateway.deserialization.impl.TrackingConnectorInfo import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer; import com.sap.sailing.server.gateway.serialization.impl.DetailedRaceInfoJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.SimpleRaceInfoJsonSerializer; +import com.sap.sse.common.Duration; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.concurrent.LockUtil; @@ -188,11 +192,13 @@ public class RemoteSailingServerSet { private void updateRemoteServerTrackedRacesCacheSynchronously(RemoteSailingServerReference ref) { Util.Pair, Exception> result; try { - final URL raceListURL = getRaceListURL(ref); - logger.fine("Updating racelist for remote server " + ref + " from URL " + raceListURL); + + logger.fine("Updating racelist for remote server " + ref + " from URL " + ref.getURL()); + final SimpleRaceInfoJsonSerializer deserializer = new SimpleRaceInfoJsonSerializer(); final Set races = new HashSet<>(); - for (Object remoteWithRaces : getJsonFromRemoteServerSynchronously(ref, raceListURL)) { + + for (Object remoteWithRaces : getJSONFromRemoteRacesListSynchronously(ref)) { JSONObject remoteWithRacesAsJson = (JSONObject) remoteWithRaces; String remoteUrlAsString = (String) remoteWithRacesAsJson .get(DetailedRaceInfoJsonSerializer.FIELD_REMOTEURL); @@ -235,7 +241,7 @@ public class RemoteSailingServerSet { } /** - * Build the URL for retrieving the remote servers race list. This is complemented by the endpoint here: + * Build the URL for retrieving the remote servers race list. This is complemented by the end point here: * {@link com.sap.sailing.server.gateway.jaxrs.api.TrackedRaceListResource#raceList(Boolean, String, String)} * @param ref the remote reference * @return the URL for retrieving the remote races. @@ -244,18 +250,6 @@ public class RemoteSailingServerSet { private URL getRaceListURL(RemoteSailingServerReference ref) throws MalformedURLException { URL remoteServerBaseURL = ref.getURL(); String endpoint = "/trackedRaces/getRaces?transitive=true"; - if (!ref.getSelectedEventIds().isEmpty()) { - endpoint += "&events="; - - Iterator iter = ref.getSelectedEventIds().iterator(); - while (iter.hasNext()) { - endpoint += iter.next().toString(); - if (iter.hasNext()) { - endpoint += ","; - } - } - endpoint += "&pred=" + (ref.isInclude() ? "incl" : "excl"); - } return getEndpointUrl(remoteServerBaseURL, endpoint); } @@ -346,6 +340,45 @@ public class RemoteSailingServerSet { updateCache(ref, result, cachedStatisticsByYearForRemoteSailingServers::put); } + private JSONArray getJSONFromRemoteRacesListSynchronously(final RemoteSailingServerReference ref) + throws MalformedURLException, IOException, ParseException { + JSONArray data; + final URL url = getRaceListURL(ref); + final StringBuffer formParams = new StringBuffer("transitive=true"); + if (!ref.getSelectedEventIds().isEmpty()) { + formParams.append("&events="); + Iterator iter = ref.getSelectedEventIds().iterator(); + while (iter.hasNext()) { + formParams.append(URLEncoder.encode(iter.next().toString(), "utf-8")); + if (iter.hasNext()) { + formParams.append(URLEncoder.encode(",", "utf-8")); + } + } + formParams.append("&pred=" + (ref.isInclude() ? "incl" : "excl")); + } + HttpURLConnection urlConnection = (HttpURLConnection) HttpUrlConnectionHelper.redirectConnection(url, + Duration.ONE_SECOND.times(1000), "POST", (connection) -> connection + .setRequestProperty("Content-Type", "application/x-www-form-urlencoded"), + Optional.of(outputStream -> { + try (OutputStreamWriter writer = new OutputStreamWriter(outputStream, "utf-8")) { + writer.write(formParams.toString()); + } + })); + final int responseCode = urlConnection.getResponseCode(); + if (responseCode == 200) { + try (BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(urlConnection.getInputStream(), "utf-8"))) { + JSONParser parser = new JSONParser(); + data = (JSONArray) parser.parse(bufferedReader); + } + } else { + // fallback to old interface when new interface was not found. + // can be removed when all servers are upgraded + data = getJsonFromRemoteServerSynchronously(ref, url); + } + return data; + } + private JSONArray getJsonFromRemoteServerSynchronously(RemoteSailingServerReference ref, final URL url) throws IOException, ParseException { logger.fine("Updating data for remote server " + ref + " from URL " + url);