Skip to content

Commit b06cfc0

Browse files
tmoskovitchzacmos
authored andcommitted
Adding support for handling rate limits errors.
1 parent 0bfae47 commit b06cfc0

File tree

13 files changed

+1334
-7
lines changed

13 files changed

+1334
-7
lines changed

README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Add this dependency to your project's POM:
4444
<dependency>
4545
<groupId>com.twitter</groupId>
4646
<artifactId>twitter-api-java-sdk</artifactId>
47-
<version>1.1.1</version>
47+
<version>1.1.2</version>
4848
</dependency>
4949
```
5050

@@ -53,7 +53,7 @@ Add this dependency to your project's POM:
5353
Add this dependency to your project's build file:
5454

5555
```groovy
56-
implementation "com.twitter:twitter-api-java-sdk:1.1.1"
56+
implementation "com.twitter:twitter-api-java-sdk:1.1.2"
5757
```
5858

5959
### Others
@@ -66,7 +66,7 @@ mvn clean package
6666

6767
Then manually install the following JARs:
6868

69-
- `target/twitter-api-java-sdk-1.1.1.jar`
69+
- `target/twitter-api-java-sdk-1.1.2.jar`
7070
- `target/lib/*.jar`
7171

7272
## Twitter Credentials
@@ -201,6 +201,28 @@ You can implement the callback `ApiClientCallback.onAfterRefreshToken()` in orde
201201
Check this [example](examples/src/main/java/com/twitter/clientlib/auth/OAuth20RefreshToken.java) of implementing `ApiClientCallback`
202202

203203

204+
## Rate limits retry mechanism
205+
206+
207+
Everyday many thousands of developers make requests to the Twitter developer platform. To help manage the sheer volume of these requests, limits are placed on the number of requests that can be made. These limits help us provide the reliable and scalable API that our developer community relies on.
208+
209+
Each of our APIs use rate limits in different ways. To learn more about these differences between platforms, please review the specific rate limit pages within our specific API sections.
210+
211+
To check connection limits response will return [three headers](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/integrate/handling-disconnections#:~:text=Rate%20limits%20and%20usage). This is useful to understand how many times you can use the rule endpoint, and how many reconnections attempts are allowed for the streaming endpoint.
212+
213+
The Java SDK provides APIs with a build-in retry mechanism to handle the rate limits. In case of getting an http error code 429, the API will check the response headers and will wait until the rate limit is reset.
214+
215+
In order to use the retry mechanism call the APIs with an additional parameter `retries` as a first argument, check the following example:
216+
217+
218+
```java
219+
int retries = 4;
220+
streamResult = apiInstance.tweets().sampleStream(retries, null, tweetFields, null, null, null, null, 0);
221+
222+
```
223+
224+
Read more about Filtered stream and [rate limits](https://developer.twitter.com/en/docs/twitter-api/tweets/filtered-stream/integrate/handling-disconnections)
225+
204226

205227
## Documentation for API Endpoints
206228

examples/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<dependency>
1717
<groupId>com.twitter</groupId>
1818
<artifactId>twitter-api-java-sdk</artifactId>
19-
<version>1.1.1</version>
19+
<version>1.1.2</version>
2020
<scope>compile</scope>
2121
</dependency>
2222
</dependencies>

examples/src/main/java/com/twitter/clientlib/HelloWorldStreaming.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public static void main(String[] args) {
7575
// BufferedReader reader = new BufferedReader(new InputStreamReader(streamResult));
7676
// String line = reader.readLine();
7777
// while (line != null) {
78+
// if(line.isEmpty()) {
79+
// System.err.println("==> " + line.isEmpty());
80+
// line = reader.readLine();
81+
// continue;
82+
// }
7883
// System.out.println(json.getGson().fromJson(line, localVarReturnType).toString());
7984
// line = reader.readLine();
8085
// }

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<artifactId>twitter-api-java-sdk</artifactId>
66
<packaging>jar</packaging>
77
<name>twitter-api-java-sdk</name>
8-
<version>1.1.1</version>
8+
<version>1.1.2</version>
99
<url>https://github.com/twitterdev/twitter-api-java-sdk</url>
1010
<description>Twitter API v2 available endpoints</description>
1111
<scm>

src/main/java/com/twitter/clientlib/ApiClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ private void init() {
232232
json = new JSON();
233233

234234
// Set default User-Agent.
235-
setUserAgent("twitter-api-java-sdk/1.1.1");
235+
setUserAgent("twitter-api-java-sdk/1.1.2");
236236

237237
authentications = new HashMap<String, Authentication>();
238238
}
@@ -1058,7 +1058,7 @@ public InputStream executeStream(Call call, Type returnType) throws ApiException
10581058
try {
10591059
Response response = call.execute();
10601060
if (!response.isSuccessful()) {
1061-
throw new ApiException(response.code(), response.message());
1061+
throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), null);
10621062
}
10631063
if (response.body() == null) {
10641064
return null;

src/main/java/com/twitter/clientlib/api/ApiCommon.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
import com.github.scribejava.core.model.OAuth2AccessToken;
2626

27+
import java.util.Calendar;
28+
import java.util.List;
29+
2730
import com.twitter.clientlib.ApiClient;
2831
import com.twitter.clientlib.ApiException;
2932

@@ -45,5 +48,38 @@ protected String[] reduceAuthNames(String[] localVarAuthNames) {
4548
protected boolean isOAUth2AutoRefreshToken() {
4649
return localVarApiClient.isOAUth2AutoRefreshToken();
4750
}
51+
52+
public boolean handleRateLimit(ApiException e, Integer retries) throws ApiException {
53+
boolean retryCall = false;
54+
if (e.getCode() == 429 && retries > 0) {
55+
long timeToWait = getTimeToWait(e);
56+
try {
57+
Thread.sleep(timeToWait);
58+
} catch (InterruptedException ex) {
59+
ex.printStackTrace();
60+
}
61+
retryCall = true;
62+
}
63+
return retryCall;
64+
}
65+
66+
long getTimeToWait(ApiException e) {
67+
long timeToWait = 1000;
68+
69+
if (isRateLimitRemaining(e)) {
70+
List<String> xRateLimitReset = e.getResponseHeaders().get("x-rate-limit-reset");
71+
if (xRateLimitReset != null && xRateLimitReset.get(0) != null) {
72+
timeToWait = Long.parseLong(
73+
xRateLimitReset.get(0)) * 1000 - Calendar.getInstance().getTimeInMillis();
74+
}
75+
}
76+
return timeToWait;
77+
}
78+
79+
boolean isRateLimitRemaining(ApiException e) {
80+
List<String> xRateLimitRemaining = e.getResponseHeaders().get("x-rate-limit-remaining");
81+
return xRateLimitRemaining != null && xRateLimitRemaining.get(0) != null
82+
&& Long.parseLong(xRateLimitRemaining.get(0)) == 0;
83+
}
4884
}
4985

src/main/java/com/twitter/clientlib/api/BookmarksApi.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,24 @@ public GenericTweetsTimelineResponse getUsersIdBookmarks(String id, Integer maxR
194194
return localVarResp != null ? localVarResp.getData() : null;
195195
}
196196

197+
/**
198+
* Calls the API using a retry mechanism to handle rate limits errors.
199+
*
200+
*/
201+
public GenericTweetsTimelineResponse getUsersIdBookmarks(Integer retries, String id, Integer maxResults, String paginationToken, Set<String> expansions, Set<String> tweetFields, Set<String> userFields, Set<String> mediaFields, Set<String> placeFields, Set<String> pollFields) throws ApiException {
202+
GenericTweetsTimelineResponse localVarResp;
203+
try{
204+
localVarResp = getUsersIdBookmarks(id, maxResults, paginationToken, expansions, tweetFields, userFields, mediaFields, placeFields, pollFields);
205+
} catch (ApiException e) {
206+
if(handleRateLimit(e, retries)) {
207+
return getUsersIdBookmarks(retries - 1, id, maxResults, paginationToken, expansions, tweetFields, userFields, mediaFields, placeFields, pollFields);
208+
} else {
209+
throw e;
210+
}
211+
}
212+
return localVarResp;
213+
}
214+
197215
/**
198216
* Bookmarks by User
199217
* Returns Tweet objects that have been bookmarked by the requesting user
@@ -351,6 +369,24 @@ public BookmarkMutationResponse postUsersIdBookmarks(AddBookmarkRequest addBookm
351369
return localVarResp != null ? localVarResp.getData() : null;
352370
}
353371

372+
/**
373+
* Calls the API using a retry mechanism to handle rate limits errors.
374+
*
375+
*/
376+
public BookmarkMutationResponse postUsersIdBookmarks(Integer retries, AddBookmarkRequest addBookmarkRequest, String id) throws ApiException {
377+
BookmarkMutationResponse localVarResp;
378+
try{
379+
localVarResp = postUsersIdBookmarks(addBookmarkRequest, id);
380+
} catch (ApiException e) {
381+
if(handleRateLimit(e, retries)) {
382+
return postUsersIdBookmarks(retries - 1, addBookmarkRequest, id);
383+
} else {
384+
throw e;
385+
}
386+
}
387+
return localVarResp;
388+
}
389+
354390
/**
355391
* Add Tweet to Bookmarks
356392
* Adds a Tweet (ID in the body) to the requesting user&#39;s (in the path) bookmarks
@@ -495,6 +531,24 @@ public BookmarkMutationResponse usersIdBookmarksDelete(String id, String tweetId
495531
return localVarResp != null ? localVarResp.getData() : null;
496532
}
497533

534+
/**
535+
* Calls the API using a retry mechanism to handle rate limits errors.
536+
*
537+
*/
538+
public BookmarkMutationResponse usersIdBookmarksDelete(Integer retries, String id, String tweetId) throws ApiException {
539+
BookmarkMutationResponse localVarResp;
540+
try{
541+
localVarResp = usersIdBookmarksDelete(id, tweetId);
542+
} catch (ApiException e) {
543+
if(handleRateLimit(e, retries)) {
544+
return usersIdBookmarksDelete(retries - 1, id, tweetId);
545+
} else {
546+
throw e;
547+
}
548+
}
549+
return localVarResp;
550+
}
551+
498552
/**
499553
* Remove a bookmarked Tweet
500554
* Removes a Tweet from the requesting user&#39;s bookmarked Tweets.

src/main/java/com/twitter/clientlib/api/ComplianceApi.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ public SingleComplianceJobResponse createBatchComplianceJob(CreateBatchComplianc
146146
return localVarResp != null ? localVarResp.getData() : null;
147147
}
148148

149+
/**
150+
* Calls the API using a retry mechanism to handle rate limits errors.
151+
*
152+
*/
153+
public SingleComplianceJobResponse createBatchComplianceJob(Integer retries, CreateBatchComplianceJobRequest createBatchComplianceJobRequest) throws ApiException {
154+
SingleComplianceJobResponse localVarResp;
155+
try{
156+
localVarResp = createBatchComplianceJob(createBatchComplianceJobRequest);
157+
} catch (ApiException e) {
158+
if(handleRateLimit(e, retries)) {
159+
return createBatchComplianceJob(retries - 1, createBatchComplianceJobRequest);
160+
} else {
161+
throw e;
162+
}
163+
}
164+
return localVarResp;
165+
}
166+
149167
/**
150168
* Create compliance job
151169
* Creates a compliance for the given job type
@@ -280,6 +298,24 @@ public SingleComplianceJobResponse getBatchComplianceJob(String id) throws ApiEx
280298
return localVarResp != null ? localVarResp.getData() : null;
281299
}
282300

301+
/**
302+
* Calls the API using a retry mechanism to handle rate limits errors.
303+
*
304+
*/
305+
public SingleComplianceJobResponse getBatchComplianceJob(Integer retries, String id) throws ApiException {
306+
SingleComplianceJobResponse localVarResp;
307+
try{
308+
localVarResp = getBatchComplianceJob(id);
309+
} catch (ApiException e) {
310+
if(handleRateLimit(e, retries)) {
311+
return getBatchComplianceJob(retries - 1, id);
312+
} else {
313+
throw e;
314+
}
315+
}
316+
return localVarResp;
317+
}
318+
283319
/**
284320
* Get compliance job
285321
* Returns a single compliance job by ID
@@ -423,6 +459,24 @@ public MultiComplianceJobResponse listBatchComplianceJobs(ComplianceJobType type
423459
return localVarResp != null ? localVarResp.getData() : null;
424460
}
425461

462+
/**
463+
* Calls the API using a retry mechanism to handle rate limits errors.
464+
*
465+
*/
466+
public MultiComplianceJobResponse listBatchComplianceJobs(Integer retries, ComplianceJobType type, ComplianceJobStatus status) throws ApiException {
467+
MultiComplianceJobResponse localVarResp;
468+
try{
469+
localVarResp = listBatchComplianceJobs(type, status);
470+
} catch (ApiException e) {
471+
if(handleRateLimit(e, retries)) {
472+
return listBatchComplianceJobs(retries - 1, type, status);
473+
} else {
474+
throw e;
475+
}
476+
}
477+
return localVarResp;
478+
}
479+
426480
/**
427481
* List compliance jobs
428482
* Returns recent compliance jobs for a given job type and optional job status

src/main/java/com/twitter/clientlib/api/GeneralApi.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,24 @@ public Object getOpenApiSpec() throws ApiException {
130130
return localVarResp != null ? localVarResp.getData() : null;
131131
}
132132

133+
/**
134+
* Calls the API using a retry mechanism to handle rate limits errors.
135+
*
136+
*/
137+
public Object getOpenApiSpec(Integer retries) throws ApiException {
138+
Object localVarResp;
139+
try{
140+
localVarResp = getOpenApiSpec();
141+
} catch (ApiException e) {
142+
if(handleRateLimit(e, retries)) {
143+
return getOpenApiSpec(retries - 1);
144+
} else {
145+
throw e;
146+
}
147+
}
148+
return localVarResp;
149+
}
150+
133151
/**
134152
* Returns the open api spec document.
135153
* Full open api spec in JSON format. (See https://github.com/OAI/OpenAPI-Specification/blob/master/README.md)

0 commit comments

Comments
 (0)