1313import java .util .Optional ;
1414import java .util .Queue ;
1515
16+ import org .apache .logging .log4j .LogManager ;
17+ import org .apache .logging .log4j .Logger ;
18+
1619import static jakarta .ws .rs .core .HttpHeaders .ACCEPT_CHARSET ;
1720import static jakarta .ws .rs .core .HttpHeaders .ACCEPT_ENCODING ;
1821
1922/** A abstract REST api task which requests the given url with the Funk Api settings. */
2023public abstract class AbstractJsonRestTask <T , R , D extends CrawlerUrlDTO >
2124 extends AbstractRestTask <T , D > {
25+ protected final transient Logger log = LogManager .getLogger (this .getClass ());
2226 protected static final String ENCODING_GZIP = "gzip" ;
2327 private static final long serialVersionUID = -1090560363478964885L ;
2428 protected final transient GsonBuilder gsonBuilder ;
@@ -41,24 +45,66 @@ protected AbstractJsonRestTask(
4145
4246 @ Override
4347 protected void processRestTarget (final D aDTO , final WebTarget aTarget ) {
44- gsonBuilder .registerTypeAdapter (getType (), getParser (aDTO ));
45- final Gson gson = gsonBuilder .create ();
46- Builder request = aTarget .request ();
47- final Optional <String > authKey = getAuthKey ();
48- if (authKey .isPresent ()) {
49- request = request .header (HEADER_AUTHORIZATION , authKey .get ());
50- }
48+ gsonBuilder .registerTypeAdapter (getType (), getParser (aDTO ));
49+ final Gson gson = gsonBuilder .create ();
5150
52- final Response response = createResponse (request , aDTO );
53-
54- if (response .getStatus () == 200 ) {
55- final String jsonOutput = response .readEntity (String .class );
56- final R responseObj = gson .fromJson (jsonOutput , getType ());
57- postProcessing (responseObj , aDTO );
58- } else {
59- handleHttpError (aDTO , aTarget .getUri (), response );
60- }
51+ Builder request = aTarget .request ();
52+ final Optional <String > authKey = getAuthKey ();
53+ if (authKey .isPresent ()) {
54+ request = request .header (HEADER_AUTHORIZATION , authKey .get ());
55+ }
56+ final int maxRetries = 3 ;
57+ int attempt = 0 ;
58+ while (attempt < maxRetries ) {
59+ attempt ++;
60+ Response response = null ;
61+ try {
62+ response = createResponse (request , aDTO );
63+ int status = response .getStatus ();
64+ if (status == 200 ) {
65+ final String jsonOutput = response .readEntity (String .class );
66+ final R responseObj = gson .fromJson (jsonOutput , getType ());
67+ postProcessing (responseObj , aDTO );
68+ return ;
69+ }
70+ if (status == 429 && attempt < maxRetries ) {
71+ final long proposalWaitMillis = getRetryAfterMillis (response ).orElse (1000L ) * attempt ;
72+ long waitMillis = proposalWaitMillis ;
73+ if (waitMillis < 100 ) {
74+ waitMillis = 100 ;
75+ } else if (waitMillis > 180000 ) {
76+ waitMillis = 180000 ;
77+ }
78+ //log.debug("Too Many Requests - propsoal: {} waiting: {} ", proposalWaitMillis, waitMillis);
79+ Thread .sleep (waitMillis );
80+ continue ;
81+ }
82+ handleHttpError (aDTO , aTarget .getUri (), response );
83+ return ;
84+ } catch (InterruptedException e ) {
85+ Thread .currentThread ().interrupt ();
86+ throw new RuntimeException ("Retry interrupted" , e );
87+ } finally {
88+ if (response != null ) {
89+ response .close ();
90+ }
91+ }
92+ }
6193 }
94+
95+ private Optional <Long > getRetryAfterMillis (Response response ) {
96+ String retryAfter = response .getHeaderString ("Retry-After" );
97+ if (retryAfter == null ) {
98+ return Optional .empty ();
99+ }
100+ try {
101+ long seconds = Long .parseLong (retryAfter );
102+ return Optional .of (seconds * 1000 );
103+ } catch (NumberFormatException e ) {
104+ return Optional .empty ();
105+ }
106+ }
107+
62108
63109 protected Response createResponse (final Builder request , final D aDTO ) {
64110 request .header (ACCEPT_CHARSET , StandardCharsets .UTF_8 );
0 commit comments