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