2929import java .util .TimeZone ;
3030import java .util .concurrent .CompletableFuture ;
3131import java .util .concurrent .Executors ;
32- import java .util .concurrent .Future ;
3332import java .util .concurrent .ScheduledExecutorService ;
3433import java .util .concurrent .TimeUnit ;
3534import java .util .function .Supplier ;
@@ -968,7 +967,7 @@ public Bundle searchWithStrictHandling(Class<? extends Resource> resourceType, M
968967 }
969968
970969 @ Override
971- public Future <Bundle > searchAsync (Duration initialPollingInterval , Class <? extends Resource > resourceType ,
970+ public CompletableFuture <Bundle > searchAsync (DelayStrategy delayStrategy , Class <? extends Resource > resourceType ,
972971 Map <String , List <String >> parameters )
973972 {
974973 Objects .requireNonNull (resourceType , "resourceType" );
@@ -980,19 +979,19 @@ public Future<Bundle> searchAsync(Duration initialPollingInterval, Class<? exten
980979 target = target .queryParam (entry .getKey (), entry .getValue ().toArray ());
981980 }
982981
983- return doSearchAsync (initialPollingInterval , target , false );
982+ return doSearchAsync (delayStrategy , target , false );
984983 }
985984
986985 @ Override
987- public Future <Bundle > searchAsync (Duration initialPollingInterval , String url )
986+ public CompletableFuture <Bundle > searchAsync (DelayStrategy delayStrategy , String url )
988987 {
989988 checkUri (url );
990989
991- return doSearchAsync (initialPollingInterval , client .target (url ), false );
990+ return doSearchAsync (delayStrategy , client .target (url ), false );
992991 }
993992
994993 @ Override
995- public Future <Bundle > searchAsyncWithStrictHandling (Duration initialPollingInterval ,
994+ public CompletableFuture <Bundle > searchAsyncWithStrictHandling (DelayStrategy delayStrategy ,
996995 Class <? extends Resource > resourceType , Map <String , List <String >> parameters )
997996 {
998997 Objects .requireNonNull (resourceType , "resourceType" );
@@ -1004,15 +1003,15 @@ public Future<Bundle> searchAsyncWithStrictHandling(Duration initialPollingInter
10041003 target = target .queryParam (entry .getKey (), entry .getValue ().toArray ());
10051004 }
10061005
1007- return doSearchAsync (initialPollingInterval , target , true );
1006+ return doSearchAsync (delayStrategy , target , true );
10081007 }
10091008
10101009 @ Override
1011- public Future <Bundle > searchAsyncWithStrictHandling (Duration initialPollingInterval , String url )
1010+ public CompletableFuture <Bundle > searchAsyncWithStrictHandling (DelayStrategy delayStrategy , String url )
10121011 {
10131012 checkUri (url );
10141013
1015- return doSearchAsync (initialPollingInterval , client .target (url ), true );
1014+ return doSearchAsync (delayStrategy , client .target (url ), true );
10161015 }
10171016
10181017 private void checkUri (String url )
@@ -1022,9 +1021,11 @@ private void checkUri(String url)
10221021 throw new RuntimeException ("url is blank" );
10231022 if (!url .startsWith (baseUrl ))
10241023 throw new RuntimeException ("url not starting with client base url" );
1024+ if (url .startsWith (baseUrl + "@" ))
1025+ throw new RuntimeException ("url starting with client base url + @" );
10251026 }
10261027
1027- private Future <Bundle > doSearchAsync (Duration initialPollingInterval , WebTarget target , boolean strict )
1028+ private CompletableFuture <Bundle > doSearchAsync (DelayStrategy delayStrategy , WebTarget target , boolean strict )
10281029 {
10291030 Builder requestBuilder = target .request ().header (Constants .HEADER_PREFER ,
10301031 Constants .HEADER_PREFER_RESPOND_ASYNC );
@@ -1044,11 +1045,22 @@ public void completed(Response response)
10441045 else if (Status .ACCEPTED .getStatusCode () == response .getStatus ())
10451046 {
10461047 String location = response .getHeaderString (HttpHeaders .LOCATION );
1047- if (location == null )
1048- throw new RuntimeException ("202 Accepted without Location header" );
1048+ if (location == null || location .isBlank ())
1049+ {
1050+ logger .warn ("202 Accepted without Location header" );
1051+
1052+ location = response .getHeaderString (HttpHeaders .CONTENT_LOCATION );
1053+ if (location == null || location .isBlank ())
1054+ throw new RuntimeException (
1055+ "202 Accepted without Location and without Content-Location header" );
1056+ else
1057+ logger .warn ("202 Accepted with Content-Location header" );
1058+ }
1059+
1060+ checkUri (location );
10491061
10501062 response .close ();
1051- pollUntilComplete (location , initialPollingInterval . toMillis () , resultFuture );
1063+ pollUntilComplete (location , delayStrategy , resultFuture );
10521064 }
10531065 else
10541066 resultFuture .completeExceptionally (handleError (response ));
@@ -1064,36 +1076,32 @@ public void failed(Throwable throwable)
10641076 return resultFuture ;
10651077 }
10661078
1067- private void pollUntilComplete (String pollUrl , long pollIntervalMillis , CompletableFuture <Bundle > resultFuture )
1079+ private void pollUntilComplete (String location , DelayStrategy delayStrategy , CompletableFuture <Bundle > resultFuture )
10681080 {
10691081 ScheduledExecutorService executor = Executors .newSingleThreadScheduledExecutor ();
10701082
10711083 Runnable poll = new Runnable ()
10721084 {
1073- private long pollInterval = pollIntervalMillis ;
1085+ private Duration delay = delayStrategy . getFirstDelay () ;
10741086
10751087 @ Override
10761088 public void run ()
10771089 {
1090+ if (resultFuture .isCancelled ())
1091+ return ;
1092+
10781093 try
10791094 {
1080- checkUri (pollUrl );
1081-
1082- Response response = client .target (pollUrl ).request ().get ();
1095+ Response response = client .target (location ).request ().get ();
10831096
10841097 if (Status .OK .getStatusCode () == response .getStatus ())
10851098 resultFuture .complete (response .readEntity (Bundle .class ));
10861099 else if (Status .ACCEPTED .getStatusCode () == response .getStatus ())
10871100 {
1088- String location = response .getHeaderString (HttpHeaders .LOCATION );
1089- if (location == null )
1090- throw new RuntimeException ("202 Accepted without Location header" );
1091-
10921101 response .close ();
10931102
1094- pollInterval *= 2 ;
1095-
1096- executor .schedule (this , pollInterval , TimeUnit .MILLISECONDS );
1103+ delay = delayStrategy .getNextDelay (delay );
1104+ executor .schedule (this , delay .toMillis (), TimeUnit .MILLISECONDS );
10971105 }
10981106 else
10991107 resultFuture .completeExceptionally (handleError (response ));
@@ -1106,7 +1114,7 @@ else if (Status.ACCEPTED.getStatusCode() == response.getStatus())
11061114 }
11071115 };
11081116
1109- executor .schedule (poll , pollIntervalMillis , TimeUnit .MILLISECONDS );
1117+ executor .schedule (poll , delayStrategy . getFirstDelay (). toMillis () , TimeUnit .MILLISECONDS );
11101118 }
11111119
11121120 @ Override
@@ -1165,23 +1173,21 @@ public StructureDefinition generateSnapshot(StructureDefinition differential)
11651173 }
11661174
11671175 @ Override
1168- public BasicDsfClient withRetry (int nTimes , Duration delay )
1176+ public BasicDsfClient withRetry (int nTimes , DelayStrategy delayStrategy )
11691177 {
11701178 if (nTimes < 0 )
11711179 throw new IllegalArgumentException ("nTimes < 0" );
1172- if (delay == null || delay .isNegative ())
1173- throw new IllegalArgumentException ("delay null or negative" );
1180+ Objects .requireNonNull (delayStrategy , "delayStrategy" );
11741181
1175- return new BasicDsfClientWithRetryImpl (this , nTimes , delay );
1182+ return new BasicDsfClientWithRetryImpl (this , nTimes , delayStrategy );
11761183 }
11771184
11781185 @ Override
1179- public BasicDsfClient withRetryForever (Duration delay )
1186+ public BasicDsfClient withRetryForever (DelayStrategy delayStrategy )
11801187 {
1181- if (delay == null || delay .isNegative ())
1182- throw new IllegalArgumentException ("delay null or negative" );
1188+ Objects .requireNonNull (delayStrategy , "delayStrategy" );
11831189
1184- return new BasicDsfClientWithRetryImpl (this , RETRY_FOREVER , delay );
1190+ return new BasicDsfClientWithRetryImpl (this , RETRY_FOREVER , delayStrategy );
11851191 }
11861192
11871193 @ Override
0 commit comments