11package io .jenkins .plugins .gitlabbranchsource .helpers ;
22
3+ import static io .jenkins .plugins .gitlabbranchsource .helpers .GitLabHelper .apiBuilderNoAccessControl ;
34import static java .awt .RenderingHints .KEY_ALPHA_INTERPOLATION ;
45import static java .awt .RenderingHints .KEY_INTERPOLATION ;
56import static java .awt .RenderingHints .VALUE_ALPHA_INTERPOLATION_QUALITY ;
4748import javax .servlet .http .HttpServletResponse ;
4849import jenkins .model .Jenkins ;
4950import org .apache .commons .lang .StringUtils ;
51+ import org .gitlab4j .api .GitLabApi ;
5052import org .kohsuke .stapler .HttpResponse ;
5153import org .kohsuke .stapler .QueryParameter ;
5254import org .kohsuke .stapler .StaplerRequest ;
@@ -94,19 +96,18 @@ public GitLabAvatarCache() {}
9496 /**
9597 * Builds the URL for the cached avatar image of the required size.
9698 *
97- * @param url the URL of the source avatar image.
98- * @param size the size of the image.
99- * @return the URL of the cached image.
99+ * @param location the external endpoint details (url/API)
100+ * @return the Jenkins URL of the cached image.
100101 */
101- public static String buildUrl (String url , String size ) {
102+ public static String buildUrl (GitLabAvatarLocation location , String size ) {
102103 Jenkins j = Jenkins .get ();
103104 GitLabAvatarCache instance = j .getExtensionList (RootAction .class ).get (GitLabAvatarCache .class );
104105 if (instance == null ) {
105106 throw new AssertionError ();
106107 }
107- String key = Util .getDigestOf (GitLabAvatarCache .class .getName () + url );
108+ String key = Util .getDigestOf (GitLabAvatarCache .class .getName () + location . toString () );
108109 // seed the cache
109- instance .getCacheEntry (key , url );
110+ instance .getCacheEntry (key , location );
110111 return UriTemplate .buildFromTemplate (j .getRootUrlFromRequest ())
111112 .literal (instance .getUrlName ())
112113 .path ("key" )
@@ -283,12 +284,10 @@ public HttpResponse doDynamic(StaplerRequest req, @QueryParameter String size) {
283284 }
284285 }
285286 final CacheEntry avatar = getCacheEntry (key , null );
286- if (avatar == null || !(avatar .url .startsWith ("http://" ) || avatar .url .startsWith ("https://" ))) {
287- // we will generate avatars if the URL is not HTTP based
288- // since the url string will not magically turn itself into a HTTP url this
289- // avatar is immutable
287+ if (avatar == null ) {
288+ // we will generate immutable avatars if cache did not get seeded for some reason
290289 return new ImageResponse (
291- generateAvatar (avatar == null ? "" : avatar . url , targetSize ),
290+ generateAvatar ("" , targetSize ),
292291 true ,
293292 System .currentTimeMillis (),
294293 "max-age=365000000, immutable, public" );
@@ -297,7 +296,8 @@ public HttpResponse doDynamic(StaplerRequest req, @QueryParameter String size) {
297296 // serve a temporary avatar until we get the remote one, no caching as we could
298297 // have the real deal
299298 // real soon now
300- return new ImageResponse (generateAvatar (avatar .url , targetSize ), true , -1L , "no-cache, public" );
299+ return new ImageResponse (
300+ generateAvatar (avatar .avatarLocation .toString (), targetSize ), true , -1L , "no-cache, public" );
301301 }
302302 long since = req .getDateHeader ("If-Modified-Since" );
303303 if (avatar .lastModified <= since ) {
@@ -313,7 +313,8 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
313313 }
314314 if (avatar .image == null ) {
315315 // we can retry in an hour
316- return new ImageResponse (generateAvatar (avatar .url , targetSize ), true , -1L , "max-age=3600, public" );
316+ return new ImageResponse (
317+ generateAvatar (avatar .avatarLocation .toString (), targetSize ), true , -1L , "max-age=3600, public" );
317318 }
318319
319320 BufferedImage image = avatar .image ;
@@ -329,30 +330,30 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
329330 * Retrieves the entry from the cache.
330331 *
331332 * @param key the cache key.
332- * @param url the URL to fetch if the entry is missing or {@code null} to
333+ * @param avatarLocation the location details to fetch the avatar from or {@code null} to
333334 * perform a read-only check.
334335 * @return the entry or {@code null} if a read-only check found no matching
335336 * entry.
336337 */
337338 @ Nullable
338- private CacheEntry getCacheEntry (@ NonNull final String key , @ Nullable final String url ) {
339+ private CacheEntry getCacheEntry (@ NonNull final String key , @ Nullable final GitLabAvatarLocation avatarLocation ) {
339340 CacheEntry entry = cache .get (key );
340341 if (entry == null ) {
341342 synchronized (serviceLock ) {
342343 entry = cache .get (key );
343344 if (entry == null ) {
344- if (url == null ) {
345+ if (avatarLocation == null ) {
345346 return null ;
346347 }
347- entry = new CacheEntry (url , service .submit (new FetchImage (url )));
348+ entry = new CacheEntry (avatarLocation , service .submit (new FetchImage (avatarLocation )));
348349 cache .put (key , entry );
349350 }
350351 }
351352 } else {
352353 if (entry .isStale ()) {
353354 synchronized (serviceLock ) {
354355 if (!entry .pending ()) {
355- entry .setFuture (service .submit (new FetchImage (entry .url )));
356+ entry .setFuture (service .submit (new FetchImage (entry .avatarLocation )));
356357 }
357358 }
358359 }
@@ -381,15 +382,15 @@ private CacheEntry getCacheEntry(@NonNull final String key, @Nullable final Stri
381382 }
382383
383384 private static class CacheEntry {
384- private final String url ;
385+ private GitLabAvatarLocation avatarLocation ;
385386 private BufferedImage image ;
386387 private long lastModified ;
387388 private long lastAccessed = -1L ;
388389
389390 private Future <CacheEntry > future ;
390391
391- public CacheEntry (String url , BufferedImage image , long lastModified ) {
392- this .url = url ;
392+ public CacheEntry (GitLabAvatarLocation avatarLocation , BufferedImage image , long lastModified ) {
393+ this .avatarLocation = avatarLocation ;
393394 if (image .getHeight () > 128 || image .getWidth () > 128 ) {
394395 // limit the amount of storage
395396 this .image = scaleImage (image , 128 );
@@ -400,15 +401,15 @@ public CacheEntry(String url, BufferedImage image, long lastModified) {
400401 this .lastModified = lastModified < 0 ? System .currentTimeMillis () : lastModified ;
401402 }
402403
403- public CacheEntry (String url , Future <CacheEntry > future ) {
404- this .url = url ;
404+ public CacheEntry (GitLabAvatarLocation avatarLocation , Future <CacheEntry > future ) {
405+ this .avatarLocation = avatarLocation ;
405406 this .image = null ;
406407 this .lastModified = System .currentTimeMillis ();
407408 this .future = future ;
408409 }
409410
410- public CacheEntry (String url ) {
411- this .url = url ;
411+ public CacheEntry (GitLabAvatarLocation avatarLocation ) {
412+ this .avatarLocation = avatarLocation ;
412413 this .lastModified = System .currentTimeMillis ();
413414 }
414415
@@ -426,6 +427,7 @@ public synchronized boolean pending() {
426427 image = pending .image ;
427428 }
428429 lastModified = pending .lastModified ;
430+ avatarLocation = pending .avatarLocation ;
429431 future = null ;
430432 return false ;
431433 } catch (InterruptedException | ExecutionException e ) {
@@ -489,23 +491,37 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
489491 }
490492
491493 private static class FetchImage implements Callable <CacheEntry > {
492- private final String url ;
494+ private final GitLabAvatarLocation avatarLocation ;
493495
494- public FetchImage (String url ) {
495- this .url = url ;
496+ public FetchImage (GitLabAvatarLocation avatarLocation ) {
497+ this .avatarLocation = avatarLocation ;
496498 }
497499
498500 @ Override
499501 public CacheEntry call () throws Exception {
500- LOGGER .log (Level .FINE , "Attempting to fetch remote avatar: {0}" , url );
502+ LOGGER .log (Level .FINE , "Attempting to fetch remote avatar: {0}" , avatarLocation . toString () );
501503 long start = System .nanoTime ();
502504 try {
503- HttpURLConnection connection = (HttpURLConnection ) new URL (url ).openConnection ();
505+ if (avatarLocation .apiAvailable ()) {
506+ try (GitLabApi apiClient = apiBuilderNoAccessControl (avatarLocation .getServerName ());
507+ InputStream is = avatarLocation .isProject ()
508+ ? apiClient .getProjectApi ().getAvatar (avatarLocation .getFullPath ())
509+ : apiClient .getGroupApi ().getAvatar (avatarLocation .getFullPath ())) {
510+ BufferedImage image = ImageIO .read (is );
511+ if (image == null ) {
512+ return new CacheEntry (avatarLocation );
513+ }
514+ return new CacheEntry (avatarLocation , image , -1 );
515+ }
516+ }
517+
518+ HttpURLConnection connection =
519+ (HttpURLConnection ) new URL (avatarLocation .getAvatarUrl ()).openConnection ();
504520 try {
505521 connection .setConnectTimeout (10000 );
506522 connection .setReadTimeout (30000 );
507523 if (!connection .getContentType ().startsWith ("image/" )) {
508- return new CacheEntry (url );
524+ return new CacheEntry (avatarLocation );
509525 }
510526 int length = connection .getContentLength ();
511527 // buffered stream should be no more than 16k if we know the length
@@ -515,21 +531,21 @@ public CacheEntry call() throws Exception {
515531 BufferedInputStream bis = new BufferedInputStream (is , length )) {
516532 BufferedImage image = ImageIO .read (bis );
517533 if (image == null ) {
518- return new CacheEntry (url );
534+ return new CacheEntry (avatarLocation );
519535 }
520- return new CacheEntry (url , image , connection .getLastModified ());
536+ return new CacheEntry (avatarLocation , image , connection .getLastModified ());
521537 }
522538 } finally {
523539 connection .disconnect ();
524540 }
525541 } catch (IOException e ) {
526542 LOGGER .log (Level .INFO , e .getMessage (), e );
527- return new CacheEntry (url );
543+ return new CacheEntry (avatarLocation );
528544 } finally {
529545 long end = System .nanoTime ();
530546 long duration = TimeUnit .NANOSECONDS .toMillis (end - start );
531547 LOGGER .log (duration > 250 ? Level .INFO : Level .FINE , "Avatar lookup of {0} took {1}ms" , new Object [] {
532- url , duration
548+ avatarLocation . toString () , duration
533549 });
534550 }
535551 }
0 commit comments