2525import java .nio .file .Files ;
2626import java .nio .file .Path ;
2727import java .util .Collections ;
28- import java .util .Objects ;
2928import java .util .concurrent .ConcurrentHashMap ;
3029import java .util .function .Supplier ;
3130
@@ -106,9 +105,7 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
106105 * <strong>Initial setup:</strong> Don't provide any files - rely on auto-discovery as repositories are accessed.
107106 * <strong>Override when needed:</strong> Create {@code prefixes-myrepoId.txt} files in {@code .mvn/rrf/} and
108107 * commit to version control.
109- * <strong>Caching:</strong> Auto-discovered prefix files are cached in the local repository with unique IDs
110- * (using {@link RepositoryIdHelper#remoteRepositoryUniqueId(RemoteRepository)}) to prevent conflicts that
111- * could cause build failures.
108+ * <strong>Caching:</strong> Auto-discovered prefix files are cached in the local repository.
112109 *
113110 * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
114111 * @configurationType {@link java.lang.Boolean}
@@ -119,6 +116,58 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
119116
120117 public static final boolean DEFAULT_ENABLED = true ;
121118
119+ /**
120+ * Configuration to skip the Prefixes filter for given request. This configuration is evaluated and if {@code true}
121+ * the prefixes remote filter will not kick in. Main use case is by filter itself, to prevent recursion during
122+ * discovery of remote prefixes file, but this also allows other components to control prefix filter discovery, while
123+ * leaving configuration like {@link #CONFIG_PROP_ENABLED} still show the "real state".
124+ *
125+ * @since 2.0.14
126+ * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
127+ * @configurationType {@link java.lang.Boolean}
128+ * @configurationRepoIdSuffix Yes
129+ * @configurationDefaultValue {@link #DEFAULT_SKIPPED}
130+ */
131+ public static final String CONFIG_PROP_SKIPPED =
132+ RemoteRepositoryFilterSourceSupport .CONFIG_PROPS_PREFIX + NAME + ".skipped" ;
133+
134+ public static final boolean DEFAULT_SKIPPED = false ;
135+
136+ /**
137+ * Configuration to allow Prefixes filter to auto-discover prefixes from mirrored repositories as well. For this to
138+ * work <em>Maven should be aware</em> that given remote repository is mirror and is usually backed by MRM. Given
139+ * multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use
140+ * {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
141+ *
142+ * @since 2.0.14
143+ * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
144+ * @configurationType {@link java.lang.Boolean}
145+ * @configurationRepoIdSuffix Yes
146+ * @configurationDefaultValue {@link #DEFAULT_USE_MIRRORED_REPOSITORIES}
147+ */
148+ public static final String CONFIG_PROP_USE_MIRRORED_REPOSITORIES =
149+ RemoteRepositoryFilterSourceSupport .CONFIG_PROPS_PREFIX + NAME + ".useMirroredRepositories" ;
150+
151+ public static final boolean DEFAULT_USE_MIRRORED_REPOSITORIES = false ;
152+
153+ /**
154+ * Configuration to allow Prefixes filter to auto-discover prefixes from repository managers as well. For this to
155+ * work <em>Maven should be aware</em> that given remote repository is backed by repository manager.
156+ * Given multiple MRM implementations messes up prefixes file, is better to just skip these. In other case, one may use
157+ * {@link #CONFIG_PROP_ENABLED} with repository ID suffix.
158+ * <em>Note: as of today, nothing sets this on remote repositories, but is added for future.</em>
159+ *
160+ * @since 2.0.14
161+ * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
162+ * @configurationType {@link java.lang.Boolean}
163+ * @configurationRepoIdSuffix Yes
164+ * @configurationDefaultValue {@link #DEFAULT_USE_REPOSITORY_MANAGERS}
165+ */
166+ public static final String CONFIG_PROP_USE_REPOSITORY_MANAGERS =
167+ RemoteRepositoryFilterSourceSupport .CONFIG_PROPS_PREFIX + NAME + ".useRepositoryManagers" ;
168+
169+ public static final boolean DEFAULT_USE_REPOSITORY_MANAGERS = false ;
170+
122171 /**
123172 * The basedir where to store filter files. If path is relative, it is resolved from local repository root.
124173 *
@@ -146,8 +195,6 @@ public final class PrefixesRemoteRepositoryFilterSource extends RemoteRepository
146195
147196 private final ConcurrentHashMap <RemoteRepository , RepositoryLayout > layouts ;
148197
149- private final ConcurrentHashMap <RemoteRepository , Boolean > ongoingUpdates ;
150-
151198 @ Inject
152199 public PrefixesRemoteRepositoryFilterSource (
153200 Supplier <MetadataResolver > metadataResolver ,
@@ -158,20 +205,26 @@ public PrefixesRemoteRepositoryFilterSource(
158205 this .repositoryLayoutProvider = requireNonNull (repositoryLayoutProvider );
159206 this .prefixes = new ConcurrentHashMap <>();
160207 this .layouts = new ConcurrentHashMap <>();
161- this .ongoingUpdates = new ConcurrentHashMap <>();
162208 }
163209
164210 @ Override
165211 protected boolean isEnabled (RepositorySystemSession session ) {
166- return ConfigUtils .getBoolean (session , DEFAULT_ENABLED , CONFIG_PROP_ENABLED );
212+ return ConfigUtils .getBoolean (session , DEFAULT_ENABLED , CONFIG_PROP_ENABLED )
213+ && !ConfigUtils .getBoolean (session , DEFAULT_SKIPPED , CONFIG_PROP_SKIPPED );
167214 }
168215
169216 private boolean isRepositoryFilteringEnabled (RepositorySystemSession session , RemoteRepository remoteRepository ) {
170217 if (isEnabled (session )) {
171218 return ConfigUtils .getBoolean (
172- session ,
173- ConfigUtils .getBoolean (session , true , CONFIG_PROP_ENABLED + ".*" ),
174- CONFIG_PROP_ENABLED + "." + remoteRepository .getId ());
219+ session ,
220+ DEFAULT_ENABLED ,
221+ CONFIG_PROP_ENABLED + "." + remoteRepository .getId (),
222+ CONFIG_PROP_ENABLED + ".*" )
223+ && !ConfigUtils .getBoolean (
224+ session ,
225+ DEFAULT_SKIPPED ,
226+ CONFIG_PROP_SKIPPED + "." + remoteRepository .getId (),
227+ CONFIG_PROP_SKIPPED + ".*" );
175228 }
176229 return false ;
177230 }
@@ -190,7 +243,7 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession
190243 * @return the layout instance of {@code null} if layout not supported.
191244 */
192245 private RepositoryLayout cacheLayout (RepositorySystemSession session , RemoteRepository remoteRepository ) {
193- return layouts .computeIfAbsent (remoteRepository , r -> {
246+ return layouts .computeIfAbsent (normalizeRemoteRepository ( session , remoteRepository ) , r -> {
194247 try {
195248 return repositoryLayoutProvider .newRepositoryLayout (session , remoteRepository );
196249 } catch (NoRepositoryLayoutException e ) {
@@ -201,22 +254,9 @@ private RepositoryLayout cacheLayout(RepositorySystemSession session, RemoteRepo
201254
202255 private PrefixTree cachePrefixTree (
203256 RepositorySystemSession session , Path basedir , RemoteRepository remoteRepository ) {
204- return ongoingUpdatesGuard (
205- remoteRepository ,
206- () -> prefixes .computeIfAbsent (
207- remoteRepository , r -> loadPrefixTree (session , basedir , remoteRepository )),
208- () -> PrefixTree .SENTINEL );
209- }
210-
211- private <T > T ongoingUpdatesGuard (RemoteRepository remoteRepository , Supplier <T > unblocked , Supplier <T > blocked ) {
212- if (!remoteRepository .isBlocked () && null == ongoingUpdates .putIfAbsent (remoteRepository , Boolean .TRUE )) {
213- try {
214- return unblocked .get ();
215- } finally {
216- ongoingUpdates .remove (remoteRepository );
217- }
218- }
219- return blocked .get ();
257+ return prefixes .computeIfAbsent (
258+ normalizeRemoteRepository (session , remoteRepository ),
259+ r -> loadPrefixTree (session , basedir , remoteRepository ));
220260 }
221261
222262 private PrefixTree loadPrefixTree (
@@ -225,8 +265,12 @@ private PrefixTree loadPrefixTree(
225265 String origin = "user-provided" ;
226266 Path filePath = resolvePrefixesFromLocalConfiguration (session , baseDir , remoteRepository );
227267 if (filePath == null ) {
228- origin = "auto-discovered" ;
229- filePath = resolvePrefixesFromRemoteRepository (session , remoteRepository );
268+ if (!supportedResolvePrefixesForRemoteRepository (session , remoteRepository )) {
269+ origin = "unsupported" ;
270+ } else {
271+ origin = "auto-discovered" ;
272+ filePath = resolvePrefixesFromRemoteRepository (session , remoteRepository );
273+ }
230274 }
231275 if (filePath != null ) {
232276 PrefixesSource prefixesSource = PrefixesSource .of (remoteRepository , filePath );
@@ -273,6 +317,18 @@ private Path resolvePrefixesFromLocalConfiguration(
273317 }
274318 }
275319
320+ private boolean supportedResolvePrefixesForRemoteRepository (
321+ RepositorySystemSession session , RemoteRepository remoteRepository ) {
322+ if (remoteRepository .isRepositoryManager ()) {
323+ return ConfigUtils .getBoolean (
324+ session , DEFAULT_USE_REPOSITORY_MANAGERS , CONFIG_PROP_USE_REPOSITORY_MANAGERS );
325+ } else {
326+ return remoteRepository .getMirroredRepositories ().isEmpty ()
327+ || ConfigUtils .getBoolean (
328+ session , DEFAULT_USE_MIRRORED_REPOSITORIES , CONFIG_PROP_USE_MIRRORED_REPOSITORIES );
329+ }
330+ }
331+
276332 private Path resolvePrefixesFromRemoteRepository (
277333 RepositorySystemSession session , RemoteRepository remoteRepository ) {
278334 MetadataResolver mr = metadataResolver .get ();
@@ -282,35 +338,22 @@ private Path resolvePrefixesFromRemoteRepository(
282338 RemoteRepository prepared = rm .aggregateRepositories (
283339 session , Collections .emptyList (), Collections .singletonList (remoteRepository ), true )
284340 .get (0 );
285- // make it unique
286- RemoteRepository unique = new RemoteRepository .Builder (prepared )
287- .setId (RepositoryIdHelper .remoteRepositoryUniqueId (remoteRepository ))
288- .build ();
289- // supplier for path
290- Supplier <Path > supplier = () -> {
291- MetadataRequest request =
292- new MetadataRequest (new DefaultMetadata (PREFIX_FILE_PATH , Metadata .Nature .RELEASE_OR_SNAPSHOT ));
293- // use unique repository; this will result in prefix (repository metadata) cached under unique id
294- request .setRepository (unique );
295- request .setDeleteLocalCopyIfMissing (true );
296- request .setFavorLocalRepository (true );
297- MetadataResult result = mr .resolveMetadata (
298- new DefaultRepositorySystemSession (session ).setTransferListener (null ),
299- Collections .singleton (request ))
300- .get (0 );
301- if (result .isResolved ()) {
302- return result .getMetadata ().getPath ();
303- } else {
304- return null ;
305- }
306- };
307-
308- // prevent recursive calls; but we need extra work if not dealing with Central (as in that case outer call
309- // shields us)
310- if (Objects .equals (prepared .getId (), unique .getId ())) {
311- return supplier .get ();
341+ // retrieve prefix as metadata from repository
342+ MetadataRequest request =
343+ new MetadataRequest (new DefaultMetadata (PREFIX_FILE_PATH , Metadata .Nature .RELEASE_OR_SNAPSHOT ));
344+ request .setRepository (prepared );
345+ request .setDeleteLocalCopyIfMissing (true );
346+ request .setFavorLocalRepository (true );
347+ MetadataResult result = mr .resolveMetadata (
348+ new DefaultRepositorySystemSession (session )
349+ .setTransferListener (null )
350+ .setConfigProperty (CONFIG_PROP_SKIPPED , Boolean .TRUE .toString ()),
351+ Collections .singleton (request ))
352+ .get (0 );
353+ if (result .isResolved ()) {
354+ return result .getMetadata ().getPath ();
312355 } else {
313- return ongoingUpdatesGuard ( unique , supplier , () -> null ) ;
356+ return null ;
314357 }
315358 }
316359 return null ;
@@ -347,15 +390,15 @@ public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadat
347390 repositoryLayout .getLocation (metadata , false ).getPath ());
348391 }
349392
350- private Result acceptPrefix (RemoteRepository remoteRepository , String path ) {
351- PrefixTree prefixTree = cachePrefixTree (session , basedir , remoteRepository );
393+ private Result acceptPrefix (RemoteRepository repository , String path ) {
394+ PrefixTree prefixTree = cachePrefixTree (session , basedir , repository );
352395 if (PrefixTree .SENTINEL == prefixTree ) {
353396 return NOT_PRESENT_RESULT ;
354397 }
355398 if (prefixTree .acceptedPath (path )) {
356- return new SimpleResult (true , "Path " + path + " allowed from " + remoteRepository );
399+ return new SimpleResult (true , "Path " + path + " allowed from " + repository );
357400 } else {
358- return new SimpleResult (false , "Prefix " + path + " NOT allowed from " + remoteRepository );
401+ return new SimpleResult (false , "Path " + path + " NOT allowed from " + repository );
359402 }
360403 }
361404 }
0 commit comments