@@ -79,7 +79,6 @@ public class GitHub {
79
79
80
80
private final Map <String ,GHUser > users = new Hashtable <String , GHUser >();
81
81
private final Map <String ,GHOrganization > orgs = new Hashtable <String , GHOrganization >();
82
-
83
82
private final String apiUrl ;
84
83
85
84
/*package*/ final RateLimitHandler rateLimitHandler ;
@@ -226,7 +225,7 @@ public HttpConnector getConnector() {
226
225
/**
227
226
* Sets the custom connector used to make requests to GitHub.
228
227
*/
229
- public void setConnector (HttpConnector connector ) {
228
+ public synchronized void setConnector (HttpConnector connector ) {
230
229
this .connector = connector ;
231
230
}
232
231
@@ -276,56 +275,92 @@ public GHRateLimit getRateLimit() throws IOException {
276
275
public GHMyself getMyself () throws IOException {
277
276
requireCredential ();
278
277
279
- GHMyself u = retrieve ().to ("/user" , GHMyself .class );
278
+ // This entire block is under synchronization to avoid the relatively common case
279
+ // where a bunch of threads try to enter this code simultaneously. While we could
280
+ // scope the synchronization separately around the map retrieval and update (or use a concurrent hash)
281
+ // map, the point is to avoid making unnecessary GH API calls, which are expensive from
282
+ // an API rate standpoint
283
+ synchronized (users ) {
284
+ GHUser cachedUser = users .get (this .login );
285
+ if (cachedUser != null && cachedUser instanceof GHMyself ) {
286
+ return (GHMyself )cachedUser ;
287
+ }
280
288
281
- u .root = this ;
282
- users .put (u .getLogin (), u );
289
+ GHMyself u = retrieve ().to ("/user" , GHMyself .class );
283
290
284
- return u ;
291
+ u .root = this ;
292
+ users .put (u .getLogin (), u );
293
+ return u ;
294
+ }
285
295
}
286
296
287
297
/**
288
298
* Obtains the object that represents the named user.
289
299
*/
290
300
public GHUser getUser (String login ) throws IOException {
291
- GHUser u = users .get (login );
292
- if (u == null ) {
293
- u = retrieve ().to ("/users/" + login , GHUser .class );
294
- u .root = this ;
295
- users .put (u .getLogin (), u );
301
+ // This entire block is under synchronization to avoid the relatively common case
302
+ // where a bunch of threads try to enter this code simultaneously. While we could
303
+ // scope the synchronization separately around the map retrieval and update (or use a concurrent hash
304
+ // map), the point is to avoid making unnecessary GH API calls, which are expensive from
305
+ // an API rate standpoint
306
+ synchronized (users ) {
307
+ GHUser u = users .get (login );
308
+ if (u == null ) {
309
+ u = retrieve ().to ("/users/" + login , GHUser .class );
310
+ u .root = this ;
311
+ users .put (u .getLogin (), u );
312
+ }
313
+ return u ;
296
314
}
297
- return u ;
298
315
}
299
316
300
317
301
318
/**
302
319
* clears all cached data in order for external changes (modifications and del
303
320
*/
304
321
public void refreshCache () {
305
- users .clear ();
306
- orgs .clear ();
322
+ synchronized (users ) {
323
+ users .clear ();
324
+ }
325
+ synchronized (orgs ) {
326
+ orgs .clear ();
327
+ }
307
328
}
308
329
309
330
/**
310
331
* Interns the given {@link GHUser}.
311
332
*/
312
333
protected GHUser getUser (GHUser orig ) throws IOException {
313
- GHUser u = users .get (orig .getLogin ());
314
- if (u ==null ) {
315
- orig .root = this ;
316
- users .put (orig .getLogin (),orig );
317
- return orig ;
334
+ // This entire block is under synchronization to avoid the relatively common case
335
+ // where a bunch of threads try to enter this code simultaneously. While we could
336
+ // scope the synchronization separately around the map retrieval and update (or use a concurrent hash
337
+ // map), the point is to avoid making unnecessary GH API calls, which are expensive from
338
+ // an API rate standpoint
339
+ synchronized (users ) {
340
+ GHUser u = users .get (orig .getLogin ());
341
+ if (u ==null ) {
342
+ orig .root = this ;
343
+ users .put (orig .getLogin (),orig );
344
+ return orig ;
345
+ }
346
+ return u ;
318
347
}
319
- return u ;
320
348
}
321
349
322
350
public GHOrganization getOrganization (String name ) throws IOException {
323
- GHOrganization o = orgs .get (name );
324
- if (o ==null ) {
325
- o = retrieve ().to ("/orgs/" + name , GHOrganization .class ).wrapUp (this );
326
- orgs .put (name ,o );
351
+ // This entire block is under synchronization to avoid the relatively common case
352
+ // where a bunch of threads try to enter this code simultaneously. While we could
353
+ // scope the synchronization separately around the map retrieval and update (or use a concurrent hash
354
+ // map), the point is to avoid making unnecessary GH API calls, which are expensive from
355
+ // an API rate standpoint
356
+ synchronized (orgs ) {
357
+ GHOrganization o = orgs .get (name );
358
+ if (o ==null ) {
359
+ o = retrieve ().to ("/orgs/" + name , GHOrganization .class ).wrapUp (this );
360
+ orgs .put (name ,o );
361
+ }
362
+ return o ;
327
363
}
328
- return o ;
329
364
}
330
365
331
366
/**
0 commit comments