4343import java .util .LinkedHashMap ;
4444import java .util .List ;
4545import java .util .Map ;
46- import java .util .Set ;
4746import java .util .TreeMap ;
47+ import java .util .concurrent .ConcurrentHashMap ;
48+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
4849import java .util .logging .Level ;
4950import java .util .logging .Logger ;
5051import edu .umd .cs .findbugs .annotations .CheckForNull ;
6566@ Extension public class LibraryAdder extends ClasspathAdder {
6667
6768 private static final Logger LOGGER = Logger .getLogger (LibraryAdder .class .getName ());
69+
70+ private static ConcurrentHashMap <String , ReentrantReadWriteLock > cacheRetrieveLock = new ConcurrentHashMap <>();
6871
72+ static @ NonNull ReentrantReadWriteLock getReadWriteLockFor (@ NonNull String name ) {
73+ return cacheRetrieveLock .computeIfAbsent (name , s -> new ReentrantReadWriteLock (true ));
74+ }
75+
6976 @ Override public List <Addition > add (CpsFlowExecution execution , List <String > libraries , HashMap <String , Boolean > changelogs ) throws Exception {
7077 Queue .Executable executable = execution .getOwner ().getExecutable ();
7178 Run <?,?> build ;
156163 }
157164 }
158165
166+ private enum CacheStatus {
167+ VALID ,
168+ DOES_NOT_EXIST ,
169+ EXPIRED ;
170+ }
171+
172+ private static CacheStatus getCacheStatus (@ NonNull LibraryCachingConfiguration cachingConfiguration , @ NonNull final FilePath versionCacheDir )
173+ throws IOException , InterruptedException
174+ {
175+ if (cachingConfiguration .isRefreshEnabled ()) {
176+ final long cachingMilliseconds = cachingConfiguration .getRefreshTimeMilliseconds ();
177+
178+ if (versionCacheDir .exists ()) {
179+ if ((versionCacheDir .lastModified () + cachingMilliseconds ) > System .currentTimeMillis ()) {
180+ return CacheStatus .VALID ;
181+ } else {
182+ return CacheStatus .EXPIRED ;
183+ }
184+ } else {
185+ return CacheStatus .DOES_NOT_EXIST ;
186+ }
187+ } else {
188+ if (versionCacheDir .exists ()) {
189+ return CacheStatus .VALID ;
190+ } else {
191+ return CacheStatus .DOES_NOT_EXIST ;
192+ }
193+ }
194+ }
195+
159196 /** Retrieve library files. */
160197 static List <URL > retrieve (@ NonNull LibraryRecord record , @ NonNull LibraryRetriever retriever , @ NonNull TaskListener listener , @ NonNull Run <?,?> run , @ NonNull CpsFlowExecution execution ) throws Exception {
161198 String name = record .name ;
@@ -165,42 +202,60 @@ static List<URL> retrieve(@NonNull LibraryRecord record, @NonNull LibraryRetriev
165202 FilePath libDir = new FilePath (execution .getOwner ().getRootDir ()).child ("libs/" + record .getDirectoryName ());
166203 Boolean shouldCache = cachingConfiguration != null ;
167204 final FilePath versionCacheDir = new FilePath (LibraryCachingConfiguration .getGlobalLibrariesCacheDir (), record .getDirectoryName ());
168- final FilePath retrieveLockFile = new FilePath ( versionCacheDir , LibraryCachingConfiguration . RETRIEVE_LOCK_FILE );
205+ ReentrantReadWriteLock retrieveLock = getReadWriteLockFor ( record . getDirectoryName () );
169206 final FilePath lastReadFile = new FilePath (versionCacheDir , LibraryCachingConfiguration .LAST_READ_FILE );
170207
171208 if (shouldCache && cachingConfiguration .isExcluded (version )) {
172209 listener .getLogger ().println ("Library " + name + "@" + version + " is excluded from caching." );
173210 shouldCache = false ;
174211 }
175212
176- if (shouldCache && retrieveLockFile .exists ()) {
177- listener .getLogger ().println ("Library " + name + "@" + version + " is currently being cached by another job, retrieving without cache." );
178- shouldCache = false ;
179- }
180-
181213 if (shouldCache ) {
182- if (cachingConfiguration .isRefreshEnabled ()) {
183- final long cachingMinutes = cachingConfiguration .getRefreshTimeMinutes ();
184- final long cachingMilliseconds = cachingConfiguration .getRefreshTimeMilliseconds ();
185-
186- if (versionCacheDir .exists () && (versionCacheDir .lastModified () + cachingMilliseconds ) < System .currentTimeMillis ()) {
187- listener .getLogger ().println ("Library " + name + "@" + version + " is due for a refresh after " + cachingMinutes + " minutes, clearing." );
188- versionCacheDir .deleteRecursive ();
214+ retrieveLock .readLock ().lockInterruptibly ();
215+ try {
216+ CacheStatus cacheStatus = getCacheStatus (cachingConfiguration , versionCacheDir );
217+ if (cacheStatus == CacheStatus .DOES_NOT_EXIST || cacheStatus == CacheStatus .EXPIRED ) {
218+ retrieveLock .readLock ().unlock ();
219+ retrieveLock .writeLock ().lockInterruptibly ();
220+ try {
221+ boolean retrieve = false ;
222+ switch (getCacheStatus (cachingConfiguration , versionCacheDir )) {
223+ case VALID :
224+ listener .getLogger ().println ("Library " + name + "@" + version + " is cached. Copying from home." );
225+ break ;
226+ case DOES_NOT_EXIST :
227+ retrieve = true ;
228+ break ;
229+ case EXPIRED :
230+ long cachingMinutes = cachingConfiguration .getRefreshTimeMinutes ();
231+ listener .getLogger ().println ("Library " + name + "@" + version + " is due for a refresh after " + cachingMinutes + " minutes, clearing." );
232+ if (versionCacheDir .exists ()) {
233+ versionCacheDir .deleteRecursive ();
234+ versionCacheDir .withSuffix ("-name.txt" ).delete ();
235+ }
236+ retrieve = true ;
237+ break ;
238+ }
239+
240+ if (retrieve ) {
241+ listener .getLogger ().println ("Caching library " + name + "@" + version );
242+ versionCacheDir .mkdirs ();
243+ retriever .retrieve (name , version , changelog , versionCacheDir , run , listener );
244+ }
245+ retrieveLock .readLock ().lock ();
246+ } finally {
247+ retrieveLock .writeLock ().unlock ();
248+ }
249+ } else {
250+ listener .getLogger ().println ("Library " + name + "@" + version + " is cached. Copying from home." );
189251 }
252+
253+ lastReadFile .touch (System .currentTimeMillis ());
254+ versionCacheDir .withSuffix ("-name.txt" ).write (name , "UTF-8" );
255+ versionCacheDir .copyRecursiveTo (libDir );
256+ } finally {
257+ retrieveLock .readLock ().unlock ();
190258 }
191-
192- if (versionCacheDir .exists ()) {
193- listener .getLogger ().println ("Library " + name + "@" + version + " is cached. Copying from home." );
194- } else {
195- listener .getLogger ().println ("Caching library " + name + "@" + version );
196- versionCacheDir .mkdirs ();
197- retrieveLockFile .touch (System .currentTimeMillis ());
198- retriever .retrieve (name , version , changelog , versionCacheDir , run , listener );
199- retrieveLockFile .delete ();
200- }
201- lastReadFile .touch (System .currentTimeMillis ());
202- versionCacheDir .withSuffix ("-name.txt" ).write (name , "UTF-8" );
203- versionCacheDir .copyRecursiveTo (libDir );
204259 } else {
205260 retriever .retrieve (name , version , changelog , libDir , run , listener );
206261 }
0 commit comments