diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java index 8fe24c1ab..b1ace216a 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,7 +20,6 @@ */ import java.util.Map; -import java.util.function.Consumer; import org.eclipse.aether.artifact.ArtifactTypeRegistry; import org.eclipse.aether.collection.DependencyGraphTransformer; @@ -41,10 +40,10 @@ /** * A special repository system session to enable decorating or proxying another session. To do so, clients have to - * create a subclass and implement {@link #getSession()}. + * create a subclass and implement {@link #getSession()}, and optionally override other methods. */ public abstract class AbstractForwardingRepositorySystemSession - implements RepositorySystemSession + implements RepositorySystemSession { /** @@ -58,7 +57,7 @@ protected AbstractForwardingRepositorySystemSession() * Gets the repository system session to which this instance forwards calls. It's worth noting that this class does * not save/cache the returned reference but queries this method before each forwarding. Hence, the session * forwarded to may change over time or depending on the context (e.g. calling thread). - * + * * @return The repository system session to forward calls to, never {@code null}. */ protected abstract RepositorySystemSession getSession(); @@ -212,34 +211,10 @@ public RepositoryCache getCache() { return getSession().getCache(); } - + @Override public FileTransformerManager getFileTransformerManager() { return getSession().getFileTransformerManager(); } - - @Override - public final void addOnCloseHandler( Consumer handler ) - { - getSession().addOnCloseHandler( handler ); - } - - @Override - public final boolean isClosed() - { - return getSession().isClosed(); - } - - /** - * This method is special: by default it throws (nested session should never be closed), the "top level" session - * should be closed instead. Still, this method is NOT {@code final}, to allow implementations overriding it, - * and in case when needed, handle forwarded session as "top level" session. - */ - @Override - public void close() - { - throw new IllegalStateException( "Forwarding session should not be closed, " - + "close the top-level (forwarded) session instead." ); - } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java index 7ed9313a5..4c0163e81 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -19,16 +19,10 @@ * under the License. */ -import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.ArtifactType; @@ -54,6 +48,8 @@ import org.eclipse.aether.transform.FileTransformer; import org.eclipse.aether.transform.FileTransformerManager; +import static java.util.Objects.requireNonNull; + /** * A simple repository system session. *

@@ -63,11 +59,9 @@ * accidental manipulation of it afterwards. */ public final class DefaultRepositorySystemSession - implements RepositorySystemSession + implements RepositorySystemSession { - private final AtomicBoolean closed; - private boolean readOnly; private boolean offline; @@ -133,7 +127,6 @@ public final class DefaultRepositorySystemSession */ public DefaultRepositorySystemSession() { - closed = new AtomicBoolean( false ); systemProperties = new HashMap<>(); systemPropertiesView = Collections.unmodifiableMap( systemProperties ); userProperties = new HashMap<>(); @@ -160,7 +153,6 @@ public DefaultRepositorySystemSession( RepositorySystemSession session ) { requireNonNull( session, "repository system session cannot be null" ); - closed = new AtomicBoolean( false ); setOffline( session.isOffline() ); setIgnoreArtifactDescriptorRepositories( session.isIgnoreArtifactDescriptorRepositories() ); setResolutionErrorPolicy( session.getResolutionErrorPolicy() ); @@ -188,6 +180,7 @@ public DefaultRepositorySystemSession( RepositorySystemSession session ) setCache( session.getCache() ); } + @Override public boolean isOffline() { return offline; @@ -196,7 +189,7 @@ public boolean isOffline() /** * Controls whether the repository system operates in offline mode and avoids/refuses any access to remote * repositories. - * + * * @param offline {@code true} if the repository system is in offline mode, {@code false} otherwise. * @return This session for chaining, never {@code null}. */ @@ -207,6 +200,7 @@ public DefaultRepositorySystemSession setOffline( boolean offline ) return this; } + @Override public boolean isIgnoreArtifactDescriptorRepositories() { return ignoreArtifactDescriptorRepositories; @@ -215,9 +209,10 @@ public boolean isIgnoreArtifactDescriptorRepositories() /** * Controls whether repositories declared in artifact descriptors should be ignored during transitive dependency * collection. If enabled, only the repositories originally provided with the collect request will be considered. - * + * * @param ignoreArtifactDescriptorRepositories {@code true} to ignore additional repositories from artifact - * descriptors, {@code false} to merge those with the originally specified repositories. + * descriptors, {@code false} to merge those with the originally + * specified repositories. * @return This session for chaining, never {@code null}. */ public DefaultRepositorySystemSession setIgnoreArtifactDescriptorRepositories( @@ -228,6 +223,7 @@ public DefaultRepositorySystemSession setIgnoreArtifactDescriptorRepositories( return this; } + @Override public ResolutionErrorPolicy getResolutionErrorPolicy() { return resolutionErrorPolicy; @@ -235,9 +231,9 @@ public ResolutionErrorPolicy getResolutionErrorPolicy() /** * Sets the policy which controls whether resolutions errors from remote repositories should be cached. - * + * * @param resolutionErrorPolicy The resolution error policy for this session, may be {@code null} if resolution - * errors should generally not be cached. + * errors should generally not be cached. * @return This session for chaining, never {@code null}. */ public DefaultRepositorySystemSession setResolutionErrorPolicy( ResolutionErrorPolicy resolutionErrorPolicy ) @@ -247,6 +243,7 @@ public DefaultRepositorySystemSession setResolutionErrorPolicy( ResolutionErrorP return this; } + @Override public ArtifactDescriptorPolicy getArtifactDescriptorPolicy() { return artifactDescriptorPolicy; @@ -254,9 +251,9 @@ public ArtifactDescriptorPolicy getArtifactDescriptorPolicy() /** * Sets the policy which controls how errors related to reading artifact descriptors should be handled. - * + * * @param artifactDescriptorPolicy The descriptor error policy for this session, may be {@code null} if descriptor - * errors should generally not be tolerated. + * errors should generally not be tolerated. * @return This session for chaining, never {@code null}. */ public DefaultRepositorySystemSession setArtifactDescriptorPolicy( @@ -267,6 +264,7 @@ public DefaultRepositorySystemSession setArtifactDescriptorPolicy( return this; } + @Override public String getChecksumPolicy() { return checksumPolicy; @@ -275,7 +273,7 @@ public String getChecksumPolicy() /** * Sets the global checksum policy. If set, the global checksum policy overrides the checksum policies of the remote * repositories being used for resolution. - * + * * @param checksumPolicy The global checksum policy, may be {@code null}/empty to apply the per-repository policies. * @return This session for chaining, never {@code null}. * @see RepositoryPolicy#CHECKSUM_POLICY_FAIL @@ -289,6 +287,7 @@ public DefaultRepositorySystemSession setChecksumPolicy( String checksumPolicy ) return this; } + @Override public String getUpdatePolicy() { return updatePolicy; @@ -297,7 +296,7 @@ public String getUpdatePolicy() /** * Sets the global update policy. If set, the global update policy overrides the update policies of the remote * repositories being used for resolution. - * + * * @param updatePolicy The global update policy, may be {@code null}/empty to apply the per-repository policies. * @return This session for chaining, never {@code null}. * @see RepositoryPolicy#UPDATE_POLICY_ALWAYS @@ -311,6 +310,7 @@ public DefaultRepositorySystemSession setUpdatePolicy( String updatePolicy ) return this; } + @Override public LocalRepository getLocalRepository() { LocalRepositoryManager lrm = getLocalRepositoryManager(); @@ -325,7 +325,7 @@ public LocalRepositoryManager getLocalRepositoryManager() /** * Sets the local repository manager used during this session. Note: Eventually, a valid session must have * a local repository manager set. - * + * * @param localRepositoryManager The local repository manager used during this session, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -353,6 +353,7 @@ public DefaultRepositorySystemSession setFileTransformerManager( FileTransformer return this; } + @Override public WorkspaceReader getWorkspaceReader() { return workspaceReader; @@ -361,7 +362,7 @@ public WorkspaceReader getWorkspaceReader() /** * Sets the workspace reader used during this session. If set, the workspace reader will usually be consulted first * to resolve artifacts. - * + * * @param workspaceReader The workspace reader for this session, may be {@code null} if none. * @return This session for chaining, never {@code null}. */ @@ -372,6 +373,7 @@ public DefaultRepositorySystemSession setWorkspaceReader( WorkspaceReader worksp return this; } + @Override public RepositoryListener getRepositoryListener() { return repositoryListener; @@ -379,7 +381,7 @@ public RepositoryListener getRepositoryListener() /** * Sets the listener being notified of actions in the repository system. - * + * * @param repositoryListener The repository listener, may be {@code null} if none. * @return This session for chaining, never {@code null}. */ @@ -390,6 +392,7 @@ public DefaultRepositorySystemSession setRepositoryListener( RepositoryListener return this; } + @Override public TransferListener getTransferListener() { return transferListener; @@ -397,7 +400,7 @@ public TransferListener getTransferListener() /** * Sets the listener being notified of uploads/downloads by the repository system. - * + * * @param transferListener The transfer listener, may be {@code null} if none. * @return This session for chaining, never {@code null}. */ @@ -435,6 +438,7 @@ private Map copySafe( Map table, Class valueType ) return map; } + @Override public Map getSystemProperties() { return systemPropertiesView; @@ -446,7 +450,7 @@ public Map getSystemProperties() *

* Note: System properties are of type {@code Map} and any key-value pair in the input map * that doesn't match this type will be silently ignored. - * + * * @param systemProperties The system properties, may be {@code null} or empty if none. * @return This session for chaining, never {@code null}. */ @@ -460,8 +464,8 @@ public DefaultRepositorySystemSession setSystemProperties( Map systemPrope /** * Sets the specified system property. - * - * @param key The property key, must not be {@code null}. + * + * @param key The property key, must not be {@code null}. * @param value The property value, may be {@code null} to remove/unset the property. * @return This session for chaining, never {@code null}. */ @@ -479,6 +483,7 @@ public DefaultRepositorySystemSession setSystemProperty( String key, String valu return this; } + @Override public Map getUserProperties() { return userPropertiesView; @@ -491,7 +496,7 @@ public Map getUserProperties() *

* Note: User properties are of type {@code Map} and any key-value pair in the input map * that doesn't match this type will be silently ignored. - * + * * @param userProperties The user properties, may be {@code null} or empty if none. * @return This session for chaining, never {@code null}. */ @@ -505,8 +510,8 @@ public DefaultRepositorySystemSession setUserProperties( Map userPropertie /** * Sets the specified user property. - * - * @param key The property key, must not be {@code null}. + * + * @param key The property key, must not be {@code null}. * @param value The property value, may be {@code null} to remove/unset the property. * @return This session for chaining, never {@code null}. */ @@ -524,6 +529,7 @@ public DefaultRepositorySystemSession setUserProperty( String key, String value return this; } + @Override public Map getConfigProperties() { return configPropertiesView; @@ -535,7 +541,7 @@ public Map getConfigProperties() *

* Note: Configuration properties are of type {@code Map} and any key-value pair in the * input map that doesn't match this type will be silently ignored. - * + * * @param configProperties The configuration properties, may be {@code null} or empty if none. * @return This session for chaining, never {@code null}. */ @@ -549,8 +555,8 @@ public DefaultRepositorySystemSession setConfigProperties( Map configPrope /** * Sets the specified configuration property. - * - * @param key The property key, must not be {@code null}. + * + * @param key The property key, must not be {@code null}. * @param value The property value, may be {@code null} to remove/unset the property. * @return This session for chaining, never {@code null}. */ @@ -568,6 +574,7 @@ public DefaultRepositorySystemSession setConfigProperty( String key, Object valu return this; } + @Override public MirrorSelector getMirrorSelector() { return mirrorSelector; @@ -577,7 +584,7 @@ public MirrorSelector getMirrorSelector() * Sets the mirror selector to use for repositories discovered in artifact descriptors. Note that this selector is * not used for remote repositories which are passed as request parameters to the repository system, those * repositories are supposed to denote the effective repositories. - * + * * @param mirrorSelector The mirror selector to use, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -592,6 +599,7 @@ public DefaultRepositorySystemSession setMirrorSelector( MirrorSelector mirrorSe return this; } + @Override public ProxySelector getProxySelector() { return proxySelector; @@ -601,7 +609,7 @@ public ProxySelector getProxySelector() * Sets the proxy selector to use for repositories discovered in artifact descriptors. Note that this selector is * not used for remote repositories which are passed as request parameters to the repository system, those * repositories are supposed to have their proxy (if any) already set. - * + * * @param proxySelector The proxy selector to use, may be {@code null}. * @return This session for chaining, never {@code null}. * @see org.eclipse.aether.repository.RemoteRepository#getProxy() @@ -617,6 +625,7 @@ public DefaultRepositorySystemSession setProxySelector( ProxySelector proxySelec return this; } + @Override public AuthenticationSelector getAuthenticationSelector() { return authenticationSelector; @@ -626,7 +635,7 @@ public AuthenticationSelector getAuthenticationSelector() * Sets the authentication selector to use for repositories discovered in artifact descriptors. Note that this * selector is not used for remote repositories which are passed as request parameters to the repository system, * those repositories are supposed to have their authentication (if any) already set. - * + * * @param authenticationSelector The authentication selector to use, may be {@code null}. * @return This session for chaining, never {@code null}. * @see org.eclipse.aether.repository.RemoteRepository#getAuthentication() @@ -642,6 +651,7 @@ public DefaultRepositorySystemSession setAuthenticationSelector( AuthenticationS return this; } + @Override public ArtifactTypeRegistry getArtifactTypeRegistry() { return artifactTypeRegistry; @@ -649,7 +659,7 @@ public ArtifactTypeRegistry getArtifactTypeRegistry() /** * Sets the registry of artifact types recognized by this session. - * + * * @param artifactTypeRegistry The artifact type registry, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -664,6 +674,7 @@ public DefaultRepositorySystemSession setArtifactTypeRegistry( ArtifactTypeRegis return this; } + @Override public DependencyTraverser getDependencyTraverser() { return dependencyTraverser; @@ -671,7 +682,7 @@ public DependencyTraverser getDependencyTraverser() /** * Sets the dependency traverser to use for building dependency graphs. - * + * * @param dependencyTraverser The dependency traverser to use for building dependency graphs, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -682,6 +693,7 @@ public DefaultRepositorySystemSession setDependencyTraverser( DependencyTraverse return this; } + @Override public DependencyManager getDependencyManager() { return dependencyManager; @@ -689,7 +701,7 @@ public DependencyManager getDependencyManager() /** * Sets the dependency manager to use for building dependency graphs. - * + * * @param dependencyManager The dependency manager to use for building dependency graphs, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -700,6 +712,7 @@ public DefaultRepositorySystemSession setDependencyManager( DependencyManager de return this; } + @Override public DependencySelector getDependencySelector() { return dependencySelector; @@ -707,7 +720,7 @@ public DependencySelector getDependencySelector() /** * Sets the dependency selector to use for building dependency graphs. - * + * * @param dependencySelector The dependency selector to use for building dependency graphs, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -718,6 +731,7 @@ public DefaultRepositorySystemSession setDependencySelector( DependencySelector return this; } + @Override public VersionFilter getVersionFilter() { return versionFilter; @@ -725,9 +739,9 @@ public VersionFilter getVersionFilter() /** * Sets the version filter to use for building dependency graphs. - * + * * @param versionFilter The version filter to use for building dependency graphs, may be {@code null} to not filter - * versions. + * versions. * @return This session for chaining, never {@code null}. */ public DefaultRepositorySystemSession setVersionFilter( VersionFilter versionFilter ) @@ -737,6 +751,7 @@ public DefaultRepositorySystemSession setVersionFilter( VersionFilter versionFil return this; } + @Override public DependencyGraphTransformer getDependencyGraphTransformer() { return dependencyGraphTransformer; @@ -744,9 +759,9 @@ public DependencyGraphTransformer getDependencyGraphTransformer() /** * Sets the dependency graph transformer to use for building dependency graphs. - * + * * @param dependencyGraphTransformer The dependency graph transformer to use for building dependency graphs, may be - * {@code null}. + * {@code null}. * @return This session for chaining, never {@code null}. */ public DefaultRepositorySystemSession setDependencyGraphTransformer( @@ -757,6 +772,7 @@ public DefaultRepositorySystemSession setDependencyGraphTransformer( return this; } + @Override public SessionData getData() { return data; @@ -764,7 +780,7 @@ public SessionData getData() /** * Sets the custom data associated with this session. - * + * * @param data The session data, may be {@code null}. * @return This session for chaining, never {@code null}. */ @@ -779,6 +795,7 @@ public DefaultRepositorySystemSession setData( SessionData data ) return this; } + @Override public RepositoryCache getCache() { return cache; @@ -786,7 +803,7 @@ public RepositoryCache getCache() /** * Sets the cache the repository system may use to save data for future reuse during the session. - * + * * @param cache The repository cache, may be {@code null} if none. * @return This session for chaining, never {@code null}. */ @@ -816,14 +833,10 @@ private void verifyStateForMutation() { throw new IllegalStateException( "repository system session is read-only" ); } - if ( closed.get() ) - { - throw new IllegalStateException( "repository system session is already closed" ); - } } static class NullProxySelector - implements ProxySelector + implements ProxySelector { public static final ProxySelector INSTANCE = new NullProxySelector(); @@ -837,7 +850,7 @@ public Proxy getProxy( RemoteRepository repository ) } static class NullMirrorSelector - implements MirrorSelector + implements MirrorSelector { public static final MirrorSelector INSTANCE = new NullMirrorSelector(); @@ -851,7 +864,7 @@ public RemoteRepository getMirror( RemoteRepository repository ) } static class NullAuthenticationSelector - implements AuthenticationSelector + implements AuthenticationSelector { public static final AuthenticationSelector INSTANCE = new NullAuthenticationSelector(); @@ -865,7 +878,7 @@ public Authentication getAuthentication( RemoteRepository repository ) } static final class NullArtifactTypeRegistry - implements ArtifactTypeRegistry + implements ArtifactTypeRegistry { public static final ArtifactTypeRegistry INSTANCE = new NullArtifactTypeRegistry(); @@ -887,45 +900,4 @@ public Collection getTransformersForArtifact( Artifact artifact return Collections.emptyList(); } } - - private final CopyOnWriteArrayList> onCloseHandlers - = new CopyOnWriteArrayList<>(); - - @Override - public void addOnCloseHandler( Consumer handler ) - { - requireNonNull( handler, "handler cannot be null" ); - if ( closed.get() ) - { - throw new IllegalStateException( "repository system session is already closed" ); - } - onCloseHandlers.add( 0, handler ); - } - - @Override - public boolean isClosed() - { - return closed.get(); - } - - @Override - public void close() - { - if ( closed.compareAndSet( false, true ) ) - { - ArrayList exceptions = new ArrayList<>(); - for ( Consumer onCloseHandler : onCloseHandlers ) - { - try - { - onCloseHandler.accept( this ); - } - catch ( Exception e ) - { - exceptions.add( e ); - } - } - MultiRuntimeException.mayThrow( "session on-close handler failures", exceptions ); - } - } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystem.java b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystem.java index e6c30f30b..be186e875 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystem.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystem.java @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -58,7 +58,10 @@ * The main entry point to the repository system and its functionality. Note that obtaining a concrete implementation of * this interface (e.g. via dependency injection, service locator, etc.) is dependent on the application and its * specific needs, please consult the online documentation for examples and directions on booting the system. - * + *

+ * When the repository system or the application integrating it is about to exit, invoke the {@link #shutdown()} to let + * resolver system perform possible resource cleanups. + * * @noimplement This interface is not intended to be implemented by clients. * @noextend This interface is not intended to be extended by clients. */ @@ -73,21 +76,21 @@ public interface RepositorySystem *

* The supplied request may also refer to a single concrete version rather than a version range. In this case * though, the result contains simply the (parsed) input version, regardless of the repositories and their contents. - * + * * @param session The repository session, must not be {@code null}. * @param request The version range request, must not be {@code null}. * @return The version range result, never {@code null}. * @throws VersionRangeResolutionException If the requested range could not be parsed. Note that an empty range does - * not raise an exception. + * not raise an exception. * @see #newResolutionRepositories(RepositorySystemSession, List) */ VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request ) - throws VersionRangeResolutionException; + throws VersionRangeResolutionException; /** * Resolves an artifact's meta version (if any) to a concrete version. For example, resolves "1.0-SNAPSHOT" to * "1.0-20090208.132618-23". - * + * * @param session The repository session, must not be {@code null}. * @param request The version request, must not be {@code null}. * @return The version result, never {@code null}. @@ -95,11 +98,11 @@ VersionRangeResult resolveVersionRange( RepositorySystemSession session, Version * @see #newResolutionRepositories(RepositorySystemSession, List) */ VersionResult resolveVersion( RepositorySystemSession session, VersionRequest request ) - throws VersionResolutionException; + throws VersionResolutionException; /** * Gets information about an artifact like its direct dependencies and potential relocations. - * + * * @param session The repository session, must not be {@code null}. * @param request The descriptor request, must not be {@code null}. * @return The descriptor result, never {@code null}. @@ -109,13 +112,13 @@ VersionResult resolveVersion( RepositorySystemSession session, VersionRequest re */ ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session, ArtifactDescriptorRequest request ) - throws ArtifactDescriptorException; + throws ArtifactDescriptorException; /** * Collects the transitive dependencies of an artifact and builds a dependency graph. Note that this operation is * only concerned about determining the coordinates of the transitive dependencies. To also resolve the actual * artifact files, use {@link #resolveDependencies(RepositorySystemSession, DependencyRequest)}. - * + * * @param session The repository session, must not be {@code null}. * @param request The collection request, must not be {@code null}. * @return The collection result, never {@code null}. @@ -128,29 +131,29 @@ ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session * @see #newResolutionRepositories(RepositorySystemSession, List) */ CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) - throws DependencyCollectionException; + throws DependencyCollectionException; /** * Collects and resolves the transitive dependencies of an artifact. This operation is essentially a combination of * {@link #collectDependencies(RepositorySystemSession, CollectRequest)} and * {@link #resolveArtifacts(RepositorySystemSession, Collection)}. - * + * * @param session The repository session, must not be {@code null}. * @param request The dependency request, must not be {@code null}. * @return The dependency result, never {@code null}. * @throws DependencyResolutionException If the dependency tree could not be built or any dependency artifact could - * not be resolved. + * not be resolved. * @see #newResolutionRepositories(RepositorySystemSession, List) */ DependencyResult resolveDependencies( RepositorySystemSession session, DependencyRequest request ) - throws DependencyResolutionException; + throws DependencyResolutionException; /** * Resolves the path for an artifact. The artifact will be downloaded to the local repository if necessary. An * artifact that is already resolved will be skipped and is not re-resolved. In general, callers must not assume any * relationship between an artifact's resolved filename and its coordinates. Note that this method assumes that any * relocations have already been processed. - * + * * @param session The repository session, must not be {@code null}. * @param request The resolution request, must not be {@code null}. * @return The resolution result, never {@code null}. @@ -159,15 +162,15 @@ DependencyResult resolveDependencies( RepositorySystemSession session, Dependenc * @see #newResolutionRepositories(RepositorySystemSession, List) */ ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest request ) - throws ArtifactResolutionException; + throws ArtifactResolutionException; /** * Resolves the paths for a collection of artifacts. Artifacts will be downloaded to the local repository if * necessary. Artifacts that are already resolved will be skipped and are not re-resolved. In general, callers must * not assume any relationship between an artifact's filename and its coordinates. Note that this method assumes * that any relocations have already been processed. - * - * @param session The repository session, must not be {@code null}. + * + * @param session The repository session, must not be {@code null}. * @param requests The resolution requests, must not be {@code null}. * @return The resolution results (in request order), never {@code null}. * @throws ArtifactResolutionException If any artifact could not be resolved. @@ -176,13 +179,13 @@ ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest */ List resolveArtifacts( RepositorySystemSession session, Collection requests ) - throws ArtifactResolutionException; + throws ArtifactResolutionException; /** * Resolves the paths for a collection of metadata. Metadata will be downloaded to the local repository if * necessary, e.g. because it hasn't been cached yet or the cache is deemed outdated. - * - * @param session The repository session, must not be {@code null}. + * + * @param session The repository session, must not be {@code null}. * @param requests The resolution requests, must not be {@code null}. * @return The resolution results (in request order), never {@code null}. * @see Metadata#getFile() @@ -193,18 +196,18 @@ List resolveMetadata( RepositorySystemSession session, /** * Installs a collection of artifacts and their accompanying metadata to the local repository. - * + * * @param session The repository session, must not be {@code null}. * @param request The installation request, must not be {@code null}. * @return The installation result, never {@code null}. * @throws InstallationException If any artifact/metadata from the request could not be installed. */ InstallResult install( RepositorySystemSession session, InstallRequest request ) - throws InstallationException; + throws InstallationException; /** * Uploads a collection of artifacts and their accompanying metadata to a remote repository. - * + * * @param session The repository session, must not be {@code null}. * @param request The deployment request, must not be {@code null}. * @return The deployment result, never {@code null}. @@ -212,29 +215,30 @@ InstallResult install( RepositorySystemSession session, InstallRequest request ) * @see #newDeploymentRepository(RepositorySystemSession, RemoteRepository) */ DeployResult deploy( RepositorySystemSession session, DeployRequest request ) - throws DeploymentException; + throws DeploymentException; /** * Creates a new manager for the specified local repository. If the specified local repository has no type, the * default local repository type of the system will be used. Note: It is expected that this method * invocation is one of the last steps of setting up a new session, in particular any configuration properties * should have been set already. - * - * @param session The repository system session from which to configure the manager, must not be {@code null}. + * + * @param session The repository system session from which to configure the manager, must not be + * {@code null}. * @param localRepository The local repository to create a manager for, must not be {@code null}. * @return The local repository manager, never {@code null}. * @throws IllegalArgumentException If the specified repository type is not recognized or no base directory is - * given. + * given. */ LocalRepositoryManager newLocalRepositoryManager( RepositorySystemSession session, LocalRepository localRepository ); /** * Creates a new synchronization context. - * + * * @param session The repository session during which the context will be used, must not be {@code null}. - * @param shared A flag indicating whether access to the artifacts/metadata associated with the new context can be - * shared among concurrent readers or whether access needs to be exclusive to the calling thread. + * @param shared A flag indicating whether access to the artifacts/metadata associated with the new context can be + * shared among concurrent readers or whether access needs to be exclusive to the calling thread. * @return The synchronization context, never {@code null}. */ SyncContext newSyncContext( RepositorySystemSession session, boolean shared ); @@ -247,13 +251,14 @@ LocalRepositoryManager newLocalRepositoryManager( RepositorySystemSession sessio * to already carry any required authentication or proxy configuration. This method can be used to apply the * authentication/proxy configuration from a session to a bare repository definition to obtain the complete * repository definition for use in the resolution request. - * - * @param session The repository system session from which to configure the repositories, must not be {@code null}. + * + * @param session The repository system session from which to configure the repositories, must not be + * {@code null}. * @param repositories The repository prototypes from which to derive the resolution repositories, must not be - * {@code null} or contain {@code null} elements. + * {@code null} or contain {@code null} elements. * @return The resolution repositories, never {@code null}. Note that there is generally no 1:1 relationship of the - * obtained repositories to the original inputs due to mirror selection potentially aggregating multiple - * repositories. + * obtained repositories to the original inputs due to mirror selection potentially aggregating multiple + * repositories. * @see #newDeploymentRepository(RepositorySystemSession, RemoteRepository) */ List newResolutionRepositories( RepositorySystemSession session, @@ -267,13 +272,34 @@ List newResolutionRepositories( RepositorySystemSession sessio * required authentication or proxy configuration. This method can be used to apply the authentication/proxy * configuration from a session to a bare repository definition to obtain the complete repository definition for use * in the deploy request. - * - * @param session The repository system session from which to configure the repository, must not be {@code null}. + * + * @param session The repository system session from which to configure the repository, must not be {@code null}. * @param repository The repository prototype from which to derive the deployment repository, must not be - * {@code null}. + * {@code null}. * @return The deployment repository, never {@code null}. * @see #newResolutionRepositories(RepositorySystemSession, List) */ RemoteRepository newDeploymentRepository( RepositorySystemSession session, RemoteRepository repository ); + /** + * Registers an "on repository system end" handler, executed after repository system is shut down. + * + * @param handler The handler, must not be {@code null}. + * @since 1.9.0 + */ + void addOnSystemEndedHandler( Runnable handler ); + + /** + * Signals to repository system to shut down. Shut down instance is not usable anymore. + *

+ * Repository system may perform some resource cleanup, if applicable. Not using this method may cause leaks or + * unclean shutdown of some subsystem. + *

+ * When shutdown happens, all the registered on-close handlers will be invoked (even if some throws), and at end + * of operation a {@link MultiRuntimeException} may be thrown, signaling that some handler(s) failed. This exception + * may be ignored, is at the discretion of caller. + * + * @since 1.9.0 + */ + void shutdown(); } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java index 204617636..df6b2fbfa 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java @@ -20,7 +20,6 @@ */ import java.util.Map; -import java.util.function.Consumer; import org.eclipse.aether.artifact.ArtifactTypeRegistry; import org.eclipse.aether.collection.DependencyGraphTransformer; @@ -45,11 +44,11 @@ * supposed to be immutable and hence can safely be shared across an entire application and any concurrent threads * reading it. Components that wish to tweak some aspects of an existing session should use the copy constructor of * {@link DefaultRepositorySystemSession} and its mutators to derive a custom session. - * + * * @noimplement This interface is not intended to be implemented by clients. * @noextend This interface is not intended to be extended by clients. */ -public interface RepositorySystemSession extends AutoCloseable +public interface RepositorySystemSession { /** @@ -271,58 +270,4 @@ public interface RepositorySystemSession extends AutoCloseable */ @Deprecated FileTransformerManager getFileTransformerManager(); - - /** - * Adds "on close" handler to this session, it must not be {@code null}. Note: when handlers are invoked, the - * passed in (this) session is ALREADY CLOSED (the {@link #isClosed()} method returns {@code true}). This implies, - * that handlers cannot use {@link RepositorySystem} to resolve/collect/and so on, handlers are meant to perform - * some internal cleanup on session close. Attempt to add handler to closed session will throw. - * - * @since 1.9.0 - */ - void addOnCloseHandler( Consumer handler ); - - /** - * Returns {@code true} if this instance was already closed. Closed sessions should NOT be used anymore. - * - * @return {@code true} if session was closed. - * @since 1.9.0 - */ - boolean isClosed(); - - /** - * Closes this session and possibly releases related resources by invoking "on close" handlers added by - * {@link #addOnCloseHandler(Consumer)} method. This method may be invoked multiple times, - * but only first invocation will actually invoke handlers, subsequent invocations will be no-op. - *

- * When close action happens, all the registered handlers will be invoked (even if some throws), and at end - * of operation a {@link MultiRuntimeException} may be thrown signaling that some handler(s) failed. This exception - * may be ignored, is at the discretion of caller. - *

- * Important: ideally it is the session creator who should be responsible to close the session. The "nested" - * (delegating, wrapped) sessions {@link AbstractForwardingRepositorySystemSession} and alike) by default - * (without overriding the {@link AbstractForwardingRepositorySystemSession#close()} method) are prevented to close - * session, and it is the "recommended" behaviour as well. On the other hand, "nested" session may receive new - * "on close" handler registrations, but those registrations are passed to delegated session, and will be invoked - * once the "top level" (delegated) session is closed. - *

- * New session "copy" instances created using copy-constructor - * {@link DefaultRepositorySystemSession#DefaultRepositorySystemSession(RepositorySystemSession)} result in new, - * independent session instances, and they do NOT share states like "read-only", "closed" and "on close handlers" - * with the copied session. Hence, they should be treated as "top level" sessions as well. - *

- * The recommended pattern for "top level" sessions is the usual try-with-resource: - * - *

 {@code
-     * try ( RepositorySystemSession session = create session... )
-     * {
-     *   ... use/nest session
-     * }
-     * }
- * - * @since 1.9.0 - */ - @Override - void close(); - } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java index 7b4e8f474..69cfc2c83 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java @@ -31,6 +31,7 @@ import static java.util.Objects.requireNonNull; import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; import org.eclipse.aether.internal.impl.DefaultArtifactResolver; @@ -229,6 +230,7 @@ public DefaultServiceLocator() addService( ChecksumAlgorithmFactorySelector.class, DefaultChecksumAlgorithmFactorySelector.class ); addService( LocalPathComposer.class, DefaultLocalPathComposer.class ); addService( RemoteRepositoryFilterManager.class, DefaultRemoteRepositoryFilterManager.class ); + addService( RepositorySystemLifecycle.class, DefaultRepositorySystemLifecycle.class ); } private Entry getEntry( Class type, boolean create ) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositorySystemLifecycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositorySystemLifecycle.java new file mode 100644 index 000000000..68fcf8147 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/RepositorySystemLifecycle.java @@ -0,0 +1,44 @@ +package org.eclipse.aether.impl; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Lifecycle managing component for repository system. + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @provisional This type is provisional and can be changed, moved or removed without prior notice. + * @since 1.9.0 + */ +public interface RepositorySystemLifecycle +{ + /** + * Marks the repository system as ended (shut down): all "on close" handlers will be invoked. This method may be + * invoked multiple times, only once will execute, subsequent calls will be no-op. + */ + void systemEnded(); + + /** + * Registers an "on repository system end" handler. + *

+ * Throws if repository system is already shut down. + */ + void addOnSystemEndedHandler( Runnable handler ); +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java index 33db93168..54e047889 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java @@ -41,7 +41,9 @@ import org.eclipse.aether.impl.RemoteRepositoryManager; import org.eclipse.aether.impl.RepositoryConnectorProvider; import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.DefaultLocalPathPrefixComposerFactory; +import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; import org.eclipse.aether.internal.impl.DefaultTrackingFileManager; @@ -64,6 +66,8 @@ import org.eclipse.aether.internal.impl.resolution.TrustedChecksumsArtifactResolverPostProcessor; import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory; import org.eclipse.aether.internal.impl.synccontext.named.NameMapper; +import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector; +import org.eclipse.aether.internal.impl.synccontext.named.ParameterizedNamedLockFactorySelector; import org.eclipse.aether.internal.impl.synccontext.named.providers.DiscriminatingNameMapperProvider; import org.eclipse.aether.internal.impl.synccontext.named.providers.FileGAVNameMapperProvider; import org.eclipse.aether.internal.impl.synccontext.named.providers.FileHashingGAVNameMapperProvider; @@ -222,6 +226,10 @@ protected void configure() bind( ChecksumAlgorithmFactorySelector.class ) .to( DefaultChecksumAlgorithmFactorySelector.class ).in ( Singleton.class ); + bind( RepositorySystemLifecycle.class ) + .to( DefaultRepositorySystemLifecycle.class ).in( Singleton.class ); + + bind( NamedLockFactorySelector.class ).toInstance( new ParameterizedNamedLockFactorySelector() ); bind( SyncContextFactory.class ).to( DefaultSyncContextFactory.class ).in( Singleton.class ); bind( org.eclipse.aether.impl.SyncContextFactory.class ) .to( org.eclipse.aether.internal.impl.synccontext.legacy.DefaultSyncContextFactory.class ) diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java index 1f7a1c2e6..7a383bf59 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java @@ -8,9 +8,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -19,15 +19,15 @@ * under the License. */ -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import static java.util.Objects.requireNonNull; - import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.RequestTrace; @@ -49,7 +49,7 @@ import org.eclipse.aether.impl.LocalRepositoryProvider; import org.eclipse.aether.impl.MetadataResolver; import org.eclipse.aether.impl.RemoteRepositoryManager; -import org.eclipse.aether.spi.synccontext.SyncContextFactory; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.impl.VersionRangeResolver; import org.eclipse.aether.impl.VersionResolver; import org.eclipse.aether.installation.InstallRequest; @@ -80,16 +80,21 @@ import org.eclipse.aether.resolution.VersionResult; import org.eclipse.aether.spi.locator.Service; import org.eclipse.aether.spi.locator.ServiceLocator; +import org.eclipse.aether.spi.synccontext.SyncContextFactory; import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor; +import static java.util.Objects.requireNonNull; + /** + * */ @Singleton @Named public class DefaultRepositorySystem - implements RepositorySystem, Service + implements RepositorySystem, Service { + private final AtomicBoolean shutdown; private VersionResolver versionResolver; @@ -113,9 +118,12 @@ public class DefaultRepositorySystem private RemoteRepositoryManager remoteRepositoryManager; + private RepositorySystemLifecycle repositorySystemLifecycle; + public DefaultRepositorySystem() { // enables default constructor + this.shutdown = new AtomicBoolean( false ); } @SuppressWarnings( "checkstyle:parameternumber" ) @@ -125,8 +133,10 @@ public DefaultRepositorySystem() ArtifactDescriptorReader artifactDescriptorReader, DependencyCollector dependencyCollector, Installer installer, Deployer deployer, LocalRepositoryProvider localRepositoryProvider, SyncContextFactory syncContextFactory, - RemoteRepositoryManager remoteRepositoryManager ) + RemoteRepositoryManager remoteRepositoryManager, + RepositorySystemLifecycle repositorySystemLifecycle ) { + this.shutdown = new AtomicBoolean( false ); setVersionResolver( versionResolver ); setVersionRangeResolver( versionRangeResolver ); setArtifactResolver( artifactResolver ); @@ -138,8 +148,10 @@ public DefaultRepositorySystem() setLocalRepositoryProvider( localRepositoryProvider ); setSyncContextFactory( syncContextFactory ); setRemoteRepositoryManager( remoteRepositoryManager ); + setRepositorySystemLifecycle( repositorySystemLifecycle ); } + @Override public void initService( ServiceLocator locator ) { setVersionResolver( locator.getService( VersionResolver.class ) ); @@ -153,6 +165,7 @@ public void initService( ServiceLocator locator ) setLocalRepositoryProvider( locator.getService( LocalRepositoryProvider.class ) ); setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) ); setSyncContextFactory( locator.getService( SyncContextFactory.class ) ); + setRepositorySystemLifecycle( locator.getService( RepositorySystemLifecycle.class ) ); } /** @@ -235,8 +248,16 @@ public DefaultRepositorySystem setRemoteRepositoryManager( RemoteRepositoryManag return this; } + public DefaultRepositorySystem setRepositorySystemLifecycle( RepositorySystemLifecycle repositorySystemLifecycle ) + { + this.repositorySystemLifecycle = requireNonNull( + repositorySystemLifecycle, "repository system lifecycle cannot be null" ); + return this; + } + + @Override public VersionResult resolveVersion( RepositorySystemSession session, VersionRequest request ) - throws VersionResolutionException + throws VersionResolutionException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -244,8 +265,9 @@ public VersionResult resolveVersion( RepositorySystemSession session, VersionReq return versionResolver.resolveVersion( session, request ); } + @Override public VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request ) - throws VersionRangeResolutionException + throws VersionRangeResolutionException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -253,9 +275,10 @@ public VersionRangeResult resolveVersionRange( RepositorySystemSession session, return versionRangeResolver.resolveVersionRange( session, request ); } + @Override public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session, ArtifactDescriptorRequest request ) - throws ArtifactDescriptorException + throws ArtifactDescriptorException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -263,8 +286,9 @@ public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession return artifactDescriptorReader.readArtifactDescriptor( session, request ); } + @Override public ArtifactResult resolveArtifact( RepositorySystemSession session, ArtifactRequest request ) - throws ArtifactResolutionException + throws ArtifactResolutionException { validateSession( session ); requireNonNull( session, "session cannot be null" ); @@ -272,9 +296,10 @@ public ArtifactResult resolveArtifact( RepositorySystemSession session, Artifact return artifactResolver.resolveArtifact( session, request ); } + @Override public List resolveArtifacts( RepositorySystemSession session, Collection requests ) - throws ArtifactResolutionException + throws ArtifactResolutionException { validateSession( session ); requireNonNull( requests, "requests cannot be null" ); @@ -282,6 +307,7 @@ public List resolveArtifacts( RepositorySystemSession session, return artifactResolver.resolveArtifacts( session, requests ); } + @Override public List resolveMetadata( RepositorySystemSession session, Collection requests ) { @@ -291,8 +317,9 @@ public List resolveMetadata( RepositorySystemSession session, return metadataResolver.resolveMetadata( session, requests ); } + @Override public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request ) - throws DependencyCollectionException + throws DependencyCollectionException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -300,8 +327,9 @@ public CollectResult collectDependencies( RepositorySystemSession session, Colle return dependencyCollector.collectDependencies( session, request ); } + @Override public DependencyResult resolveDependencies( RepositorySystemSession session, DependencyRequest request ) - throws DependencyResolutionException + throws DependencyResolutionException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -389,8 +417,9 @@ private void updateNodesWithResolvedArtifacts( List results ) } } + @Override public InstallResult install( RepositorySystemSession session, InstallRequest request ) - throws InstallationException + throws InstallationException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -398,8 +427,9 @@ public InstallResult install( RepositorySystemSession session, InstallRequest re return installer.install( session, request ); } + @Override public DeployResult deploy( RepositorySystemSession session, DeployRequest request ) - throws DeploymentException + throws DeploymentException { validateSession( session ); requireNonNull( request, "request cannot be null" ); @@ -407,6 +437,7 @@ public DeployResult deploy( RepositorySystemSession session, DeployRequest reque return deployer.deploy( session, request ); } + @Override public LocalRepositoryManager newLocalRepositoryManager( RepositorySystemSession session, LocalRepository localRepository ) { @@ -423,12 +454,14 @@ public LocalRepositoryManager newLocalRepositoryManager( RepositorySystemSession } } + @Override public SyncContext newSyncContext( RepositorySystemSession session, boolean shared ) { validateSession( session ); return syncContextFactory.newInstance( session, shared ); } + @Override public List newResolutionRepositories( RepositorySystemSession session, List repositories ) { @@ -436,11 +469,12 @@ public List newResolutionRepositories( RepositorySystemSession validateRepositories( repositories ); repositories = - remoteRepositoryManager.aggregateRepositories( session, new ArrayList(), repositories, - true ); + remoteRepositoryManager.aggregateRepositories( session, new ArrayList(), repositories, + true ); return repositories; } + @Override public RemoteRepository newDeploymentRepository( RepositorySystemSession session, RemoteRepository repository ) { validateSession( session ); @@ -454,6 +488,21 @@ public RemoteRepository newDeploymentRepository( RepositorySystemSession session return builder.build(); } + @Override + public void addOnSystemEndedHandler( Runnable handler ) + { + repositorySystemLifecycle.addOnSystemEndedHandler( handler ); + } + + @Override + public void shutdown() + { + if ( shutdown.compareAndSet( false, true ) ) + { + repositorySystemLifecycle.systemEnded(); + } + } + private void validateSession( RepositorySystemSession session ) { requireNonNull( session, "repository system session cannot be null" ); @@ -466,16 +515,16 @@ private void validateSession( RepositorySystemSession session ) invalidSession( session.getAuthenticationSelector(), "authentication selector" ); invalidSession( session.getArtifactTypeRegistry(), "artifact type registry" ); invalidSession( session.getData(), "data" ); - if ( session.isClosed() ) + if ( shutdown.get() ) { - throw new IllegalStateException( "session is already closed" ); + throw new IllegalStateException( "repository system is already shut down" ); } } private void validateRepositories( List repositories ) { requireNonNull( repositories, "repositories cannot be null" ); - for ( RemoteRepository repository: repositories ) + for ( RemoteRepository repository : repositories ) { requireNonNull( repository, "repository cannot be null" ); } @@ -485,5 +534,4 @@ private void invalidSession( Object obj, String name ) { requireNonNull( obj, "repository system session's " + name + " cannot be null" ); } - } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemLifecycle.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemLifecycle.java new file mode 100644 index 000000000..6aef7c0bc --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemLifecycle.java @@ -0,0 +1,90 @@ +package org.eclipse.aether.internal.impl; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.eclipse.aether.MultiRuntimeException; +import org.eclipse.aether.impl.RepositorySystemLifecycle; + +import static java.util.Objects.requireNonNull; + +/** + * + */ +@Singleton +@Named +public class DefaultRepositorySystemLifecycle + implements RepositorySystemLifecycle +{ + private final AtomicBoolean shutdown; + + private final CopyOnWriteArrayList onSystemEndedHandlers; + + @Inject + public DefaultRepositorySystemLifecycle() + { + this.shutdown = new AtomicBoolean( false ); + this.onSystemEndedHandlers = new CopyOnWriteArrayList<>(); + } + + @Override + public void systemEnded() + { + if ( shutdown.compareAndSet( false, true ) ) + { + final ArrayList exceptions = new ArrayList<>(); + for ( Runnable onCloseHandler : onSystemEndedHandlers ) + { + try + { + onCloseHandler.run(); + } + catch ( Exception e ) + { + exceptions.add( e ); + } + } + MultiRuntimeException.mayThrow( "system on-close handler failures", exceptions ); + } + } + + @Override + public void addOnSystemEndedHandler( Runnable handler ) + { + requireNonNull( handler, "handler cannot be null" ); + requireNotShutdown(); + onSystemEndedHandlers.add( 0, handler ); + } + + private void requireNotShutdown() + { + if ( shutdown.get() ) + { + throw new IllegalStateException( "repository system is already shut down" ); + } + } +} diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java index 1143afeb4..3c5af62e2 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSource.java @@ -53,7 +53,7 @@ * up. This implementation can be simultaneously used to lookup and also write checksums. The written checksums * will become visible across all sessions right after the moment they were written. *

- * The name of this implementation is "sparse-directory". + * The name of this implementation is "sparseDirectory". * * @see LocalPathComposer * @since 1.9.0 @@ -63,7 +63,7 @@ public final class SparseDirectoryTrustedChecksumsSource extends FileTrustedChecksumsSourceSupport { - public static final String NAME = "sparse-directory"; + public static final String NAME = "sparseDirectory"; private static final Logger LOGGER = LoggerFactory.getLogger( SparseDirectoryTrustedChecksumsSource.class ); @@ -167,11 +167,5 @@ public void addTrustedArtifactChecksums( Artifact artifact, fileProcessor.writeChecksum( checksumPath.toFile(), checksum ); } } - - @Override - public void close() - { - // nop - } - } + } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java index 05ceef90b..2bfb722f8 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java @@ -40,10 +40,10 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.LocalPathComposer; import org.eclipse.aether.repository.ArtifactRepository; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; -import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,49 +74,49 @@ *

  • To verify artifacts using summary file: {@code sha256sum --quiet -c checksums-central.sha256}
  • * *

    - * The checksums summary file is lazily loaded and remains cached in session, so file changes during lifecycle of the - * session are not picked up. This implementation can be simultaneously used to lookup and also write checksums. The - * written checksums will become visible only for writer session, and newly written checksums, if any, will be flushed - * at session end, merged with existing ones on disk, unless {@code truncateOnSave} is enabled. + * The checksums summary file is lazily loaded and remains cached during lifetime of the component, so file changes + * during lifecycle of the component are not picked up. This implementation can be simultaneously used to lookup and + * also record checksums. The recorded checksums will become visible for every session, and will be flushed + * at repository system shutdown, merged with existing ones on disk. *

    - * The name of this implementation is "summary-file". + * The name of this implementation is "summaryFile". * - * @since 1.9.0 * @see sha1sum man page * @see GNU Coreutils: md5sum + * @since 1.9.0 */ @Singleton @Named( SummaryFileTrustedChecksumsSource.NAME ) public final class SummaryFileTrustedChecksumsSource extends FileTrustedChecksumsSourceSupport { - public static final String NAME = "summary-file"; + public static final String NAME = "summaryFile"; private static final String CHECKSUMS_FILE_PREFIX = "checksums"; - private static final String CONF_NAME_TRUNCATE_ON_SAVE = "truncateOnSave"; + private static final Logger LOGGER = LoggerFactory.getLogger( SummaryFileTrustedChecksumsSource.class ); - /** - * Session key for path -> artifactId -> checksum nested map. The trick is that 1st level key "path" composition - * may change across sessions, based on session being origin aware or not. - */ - private static final String CHECKSUMS_KEY = SummaryFileTrustedChecksumsSource.class.getName() + ".checksums"; + private final LocalPathComposer localPathComposer; - private static final String ON_CLOSE_HANDLER_REG_KEY = SummaryFileTrustedChecksumsSource.class.getName() - + ".onCloseHandlerRwg"; + private final RepositorySystemLifecycle repositorySystemLifecycle; - private static final String NEW_CHECKSUMS_RECORDED_KEY = SummaryFileTrustedChecksumsSource.class.getName() - + ".newChecksumsRecorded"; + private final ConcurrentHashMap> checksums; - private static final Logger LOGGER = LoggerFactory.getLogger( SummaryFileTrustedChecksumsSource.class ); + private final ConcurrentHashMap changedChecksums; + + private final AtomicBoolean onShutdownHandlerRegistered; - private final LocalPathComposer localPathComposer; @Inject - public SummaryFileTrustedChecksumsSource( LocalPathComposer localPathComposer ) + public SummaryFileTrustedChecksumsSource( LocalPathComposer localPathComposer, + RepositorySystemLifecycle repositorySystemLifecycle ) { super( NAME ); this.localPathComposer = requireNonNull( localPathComposer ); + this.repositorySystemLifecycle = requireNonNull( repositorySystemLifecycle ); + this.checksums = new ConcurrentHashMap<>(); + this.changedChecksums = new ConcurrentHashMap<>(); + this.onShutdownHandlerRegistered = new AtomicBoolean( false ); } @Override @@ -124,18 +124,17 @@ protected Map doGetTrustedArtifactChecksums( RepositorySystemSession session, Artifact artifact, ArtifactRepository artifactRepository, List checksumAlgorithmFactories ) { - final HashMap checksums = new HashMap<>(); + final HashMap result = new HashMap<>(); final Path basedir = getBasedir( session, false ); if ( Files.isDirectory( basedir ) ) { final String artifactPath = localPathComposer.getPathForArtifact( artifact, false ); final boolean originAware = isOriginAware( session ); - final ConcurrentHashMap> cache = cache( session ); for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories ) { Path summaryFile = summaryFile( basedir, originAware, artifactRepository.getId(), checksumAlgorithmFactory.getFileExtension() ); - ConcurrentHashMap algorithmChecksums = cache.computeIfAbsent( summaryFile, f -> + ConcurrentHashMap algorithmChecksums = checksums.computeIfAbsent( summaryFile, f -> { ConcurrentHashMap loaded = loadProvidedChecksums( summaryFile ); if ( Files.isRegularFile( summaryFile ) ) @@ -149,29 +148,21 @@ protected Map doGetTrustedArtifactChecksums( String checksum = algorithmChecksums.get( artifactPath ); if ( checksum != null ) { - checksums.put( checksumAlgorithmFactory.getName(), checksum ); + result.put( checksumAlgorithmFactory.getName(), checksum ); } } } - return checksums; + return result; } @Override protected SummaryFileWriter doGetTrustedArtifactChecksumsWriter( RepositorySystemSession session ) { - if ( onCloseHandlerRegistered( session ).compareAndSet( false, true ) ) + if ( onShutdownHandlerRegistered.compareAndSet( false, true ) ) { - session.addOnCloseHandler( this::saveSessionRecordedLines ); + repositorySystemLifecycle.addOnSystemEndedHandler( this::saveRecordedLines ); } - return new SummaryFileWriter( session, cache( session ), getBasedir( session, true ), - isOriginAware( session ) ); - } - - @SuppressWarnings( "unchecked" ) - private ConcurrentHashMap> cache( RepositorySystemSession session ) - { - return (ConcurrentHashMap>) session.getData() - .computeIfAbsent( CHECKSUMS_KEY, ConcurrentHashMap::new ); + return new SummaryFileWriter( checksums, getBasedir( session, true ), isOriginAware( session ) ); } /** @@ -240,19 +231,16 @@ private ConcurrentHashMap loadProvidedChecksums( Path summaryFil private class SummaryFileWriter implements Writer { - private final RepositorySystemSession session; private final ConcurrentHashMap> cache; private final Path basedir; private final boolean originAware; - private SummaryFileWriter( RepositorySystemSession session, - ConcurrentHashMap> cache, + private SummaryFileWriter( ConcurrentHashMap> cache, Path basedir, boolean originAware ) { - this.session = session; this.cache = cache; this.basedir = basedir; this.originAware = originAware; @@ -277,78 +265,43 @@ public void addTrustedArtifactChecksums( Artifact artifact, if ( oldChecksum == null ) { - newChecksumsRecorded( session ).set( true ); // new + changedChecksums.put( summaryFile, Boolean.TRUE ); // new } else if ( !Objects.equals( oldChecksum, checksum ) ) { - newChecksumsRecorded( session ).set( true ); // updated + changedChecksums.put( summaryFile, Boolean.TRUE ); // replaced LOGGER.info( "Trusted checksum for artifact {} replaced: old {}, new {}", artifact, oldChecksum, checksum ); } } } - - @Override - public void close() - { - // nop - } - } - - /** - * Returns {@code true} if on save existing checksums file should be truncated. Otherwise, existing file is merged - * with newly recorded checksums. - *

    - * Default value is {@code false}. - */ - private boolean isTruncateOnSave( RepositorySystemSession session ) - { - return ConfigUtils.getBoolean( session, false, configPropKey( CONF_NAME_TRUNCATE_ON_SAVE ) ); - } - - /** - * Flag to preserve on-close handler registration state. - */ - private AtomicBoolean onCloseHandlerRegistered( RepositorySystemSession session ) - { - return (AtomicBoolean) session.getData().computeIfAbsent( ON_CLOSE_HANDLER_REG_KEY, - () -> new AtomicBoolean( false ) ); - } - - /** - * Flag to preserve new checksums recorded state. - */ - private AtomicBoolean newChecksumsRecorded( RepositorySystemSession session ) - { - return (AtomicBoolean) session.getData().computeIfAbsent( NEW_CHECKSUMS_RECORDED_KEY, - () -> new AtomicBoolean( false ) ); } /** * On-close handler that saves recorded checksums, if any. */ - private void saveSessionRecordedLines( RepositorySystemSession session ) + private void saveRecordedLines() { - if ( !newChecksumsRecorded( session ).get() ) + if ( changedChecksums.isEmpty() ) { return; } - Map> cache = cache( session ); ArrayList exceptions = new ArrayList<>(); - for ( Map.Entry> entry : cache.entrySet() ) + for ( Map.Entry> entry : checksums.entrySet() ) { Path summaryFile = entry.getKey(); + if ( changedChecksums.get( summaryFile ) != Boolean.TRUE ) + { + continue; + } ConcurrentHashMap recordedLines = entry.getValue(); if ( !recordedLines.isEmpty() ) { try { ConcurrentHashMap result = new ConcurrentHashMap<>(); - if ( !isTruncateOnSave( session ) ) - { - result.putAll( loadProvidedChecksums( summaryFile ) ); - } + result.putAll( loadProvidedChecksums( summaryFile ) ); result.putAll( recordedLines ); LOGGER.info( "Saving {} checksums to '{}'", result.size(), summaryFile ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java index 0897cc683..d54fc4d8b 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java @@ -98,8 +98,8 @@ protected DependencyCollectorDelegate() } protected DependencyCollectorDelegate( RemoteRepositoryManager remoteRepositoryManager, - ArtifactDescriptorReader artifactDescriptorReader, - VersionRangeResolver versionRangeResolver ) + ArtifactDescriptorReader artifactDescriptorReader, + VersionRangeResolver versionRangeResolver ) { setRemoteRepositoryManager( remoteRepositoryManager ); setArtifactDescriptorReader( artifactDescriptorReader ); diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java index 14c7a2acc..28df92342 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSource.java @@ -41,6 +41,7 @@ import org.eclipse.aether.MultiRuntimeException; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactResult; @@ -51,6 +52,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.util.Objects.requireNonNull; + /** * Remote repository filter source filtering on G coordinate. It is backed by a file that lists all allowed groupIds * and groupId not present in this file are filtered out. @@ -62,8 +65,8 @@ *

    * The groupId file is expected on path "${basedir}/groupId-${repository.id}.txt". *

    - * The groupId file once loaded are cached in session, so in-flight groupId file change during session are NOT - * noticed. + * The groupId file once loaded are cached in component, so in-flight groupId file change during component existence + * are NOT noticed. * * @since 1.9.0 */ @@ -77,29 +80,28 @@ public final class GroupIdRemoteRepositoryFilterSource private static final String CONF_NAME_RECORD = "record"; - private static final String CONF_NAME_TRUNCATE_ON_SAVE = "truncateOnSave"; - static final String GROUP_ID_FILE_PREFIX = "groupId-"; static final String GROUP_ID_FILE_SUFFIX = ".txt"; - /** - * Key of rules cache in session data as remoteRepository.id -> Set(groupID). - */ - private static final String RULES_KEY = GroupIdRemoteRepositoryFilterSource.class.getName() + ".rules"; + private static final Logger LOGGER = LoggerFactory.getLogger( GroupIdRemoteRepositoryFilterSource.class ); - private static final String ON_CLOSE_HANDLER_REG_KEY = GroupIdRemoteRepositoryFilterSource.class.getName() - + ".onCloseHandlerReg"; + private final RepositorySystemLifecycle repositorySystemLifecycle; - private static final String NEW_GROUP_ID_RECORDED_KEY = GroupIdRemoteRepositoryFilterSource.class.getName() - + ".newGroupIdRecorded"; + private final ConcurrentHashMap> rules; - private static final Logger LOGGER = LoggerFactory.getLogger( GroupIdRemoteRepositoryFilterSource.class ); + private final ConcurrentHashMap changedRules; + + private final AtomicBoolean onShutdownHandlerRegistered; @Inject - public GroupIdRemoteRepositoryFilterSource() + public GroupIdRemoteRepositoryFilterSource( RepositorySystemLifecycle repositorySystemLifecycle ) { super( NAME ); + this.repositorySystemLifecycle = requireNonNull( repositorySystemLifecycle ); + this.rules = new ConcurrentHashMap<>(); + this.changedRules = new ConcurrentHashMap<>(); + this.onShutdownHandlerRegistered = new AtomicBoolean( false ); } @Override @@ -107,10 +109,7 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession { if ( isEnabled( session ) && !isRecord( session ) ) { - if ( Files.isDirectory( getBasedir( session, false ) ) ) - { - return new GroupIdFilter( session ); - } + return new GroupIdFilter( session ); } return null; } @@ -120,21 +119,22 @@ public void postProcess( RepositorySystemSession session, List a { if ( isEnabled( session ) && isRecord( session ) ) { - if ( onCloseHandlerRegistered( session ).compareAndSet( false, true ) ) + if ( onShutdownHandlerRegistered.compareAndSet( false, true ) ) { - session.addOnCloseHandler( this::saveSessionRecordedLines ); + repositorySystemLifecycle.addOnSystemEndedHandler( this::saveRecordedLines ); } - ConcurrentHashMap> cache = cache( session ); for ( ArtifactResult artifactResult : artifactResults ) { if ( artifactResult.isResolved() && artifactResult.getRepository() instanceof RemoteRepository ) { - boolean newGroupId = cache.computeIfAbsent( artifactResult.getRepository().getId(), - f -> Collections.synchronizedSet( new TreeSet<>() ) ) - .add( artifactResult.getArtifact().getGroupId() ); + Path filePath = filePath( getBasedir( session, false ), + artifactResult.getRepository().getId() ); + boolean newGroupId = + rules.computeIfAbsent( filePath, f -> Collections.synchronizedSet( new TreeSet<>() ) ) + .add( artifactResult.getArtifact().getGroupId() ); if ( newGroupId ) { - newGroupIdRecorded( session ).set( true ); + changedRules.put( filePath, Boolean.TRUE ); } } } @@ -150,20 +150,13 @@ private Path filePath( Path basedir, String remoteRepositoryId ) GROUP_ID_FILE_PREFIX + remoteRepositoryId + GROUP_ID_FILE_SUFFIX ); } - @SuppressWarnings( "unchecked" ) - private ConcurrentHashMap> cache( RepositorySystemSession session ) - { - return ( (ConcurrentHashMap>) session.getData() - .computeIfAbsent( RULES_KEY, ConcurrentHashMap::new ) ); - } - private Set cacheRules( RepositorySystemSession session, RemoteRepository remoteRepository ) { - return cache( session ).computeIfAbsent( remoteRepository.getId(), id -> + Path filePath = filePath( getBasedir( session, false ), remoteRepository.getId() ); + return rules.computeIfAbsent( filePath, r -> { - Set rules = loadRepositoryRules( - filePath( getBasedir( session, false ), remoteRepository.getId() ) ); + Set rules = loadRepositoryRules( filePath ); if ( rules != NOT_PRESENT ) { LOGGER.info( "Loaded {} groupId for remote repository {}", rules.size(), @@ -255,58 +248,34 @@ private boolean isRecord( RepositorySystemSession session ) } /** - * Flag to preserve on-close handler registration state. + * On-close handler that saves recorded rules, if any. */ - private AtomicBoolean onCloseHandlerRegistered( RepositorySystemSession session ) + private void saveRecordedLines() { - return (AtomicBoolean) session.getData().computeIfAbsent( ON_CLOSE_HANDLER_REG_KEY, - () -> new AtomicBoolean( false ) ); - } - - /** - * Flag to preserve new groupId recorded state. - */ - private AtomicBoolean newGroupIdRecorded( RepositorySystemSession session ) - { - return (AtomicBoolean) session.getData().computeIfAbsent( NEW_GROUP_ID_RECORDED_KEY, - () -> new AtomicBoolean( false ) ); - } - - /** - * Returns {@code true} if truncate requested by session. - */ - private boolean isTruncateOnSave( RepositorySystemSession session ) - { - return ConfigUtils.getBoolean( session, false, configPropKey( CONF_NAME_TRUNCATE_ON_SAVE ) ); - } - - private void saveSessionRecordedLines( RepositorySystemSession session ) - { - Map> recorded = cache ( session ); - if ( !newGroupIdRecorded( session ).get() ) + if ( changedRules.isEmpty() ) { return; } - Path basedir = getBasedir( session, true ); ArrayList exceptions = new ArrayList<>(); - for ( Map.Entry> entry : recorded.entrySet() ) + for ( Map.Entry> entry : rules.entrySet() ) { - Path groupIdPath = filePath( basedir, entry.getKey() ); + Path filePath = entry.getKey(); + if ( changedRules.get( filePath ) != Boolean.TRUE ) + { + continue; + } Set recordedLines = entry.getValue(); if ( !recordedLines.isEmpty() ) { try { TreeSet result = new TreeSet<>(); - if ( !isTruncateOnSave( session ) ) - { - result.addAll( loadRepositoryRules( groupIdPath ) ); - } + result.addAll( loadRepositoryRules( filePath ) ); result.addAll( recordedLines ); - LOGGER.info( "Saving {} groupIds to '{}'", result.size(), groupIdPath ); - FileUtils.writeFileWithBackup( groupIdPath, p -> Files.write( p, result ) ); + LOGGER.info( "Saving {} groupIds to '{}'", result.size(), filePath ); + FileUtils.writeFileWithBackup( filePath, p -> Files.write( p, result ) ); } catch ( IOException e ) { diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java index 087f425cb..dbd94835d 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/PrefixesRemoteRepositoryFilterSource.java @@ -61,7 +61,7 @@ *

    * The prefix file is expected on path "${basedir}/prefixes-${repository.id}.txt". *

    - * The prefixes file once loaded are cached in session, so in-flight prefixes file change during session are NOT + * The prefixes file is once loaded and cached, so in-flight prefixes file change during component existence are not * noticed. *

    * Examples of published prefix files: @@ -84,19 +84,21 @@ public final class PrefixesRemoteRepositoryFilterSource static final String PREFIXES_FILE_SUFFIX = ".txt"; - private static final String LAYOUT_CACHE_KEY = PrefixesRemoteRepositoryFilterSource.class.getName() + ".layouts"; - - private static final String NODE_CACHE_KEY = PrefixesRemoteRepositoryFilterSource.class.getName() + ".nodes"; - private static final Logger LOGGER = LoggerFactory.getLogger( PrefixesRemoteRepositoryFilterSource.class ); private final RepositoryLayoutProvider repositoryLayoutProvider; + private final ConcurrentHashMap prefixes; + + private final ConcurrentHashMap layouts; + @Inject public PrefixesRemoteRepositoryFilterSource( RepositoryLayoutProvider repositoryLayoutProvider ) { super( NAME ); this.repositoryLayoutProvider = requireNonNull( repositoryLayoutProvider ); + this.prefixes = new ConcurrentHashMap<>(); + this.layouts = new ConcurrentHashMap<>(); } @Override @@ -104,48 +106,36 @@ public RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession { if ( isEnabled( session ) ) { - final Path basedir = getBasedir( session, false ); - if ( Files.isDirectory( basedir ) ) - { - return new PrefixesFilter( session, basedir ); - } + return new PrefixesFilter( session, getBasedir( session, false ) ); } return null; } /** - * Caches layout instances for remote repository within one session. + * Caches layout instances for remote repository. */ - @SuppressWarnings( "unchecked" ) - private RepositoryLayout cacheLayout( RepositorySystemSession session, - RemoteRepository remoteRepository ) + private RepositoryLayout cacheLayout( RepositorySystemSession session, RemoteRepository remoteRepository ) { - return ( (ConcurrentHashMap) session.getData() - .computeIfAbsent( LAYOUT_CACHE_KEY, ConcurrentHashMap::new ) ) - .computeIfAbsent( remoteRepository.getId(), r -> - { - try - { - return repositoryLayoutProvider.newRepositoryLayout( session, remoteRepository ); - } - catch ( NoRepositoryLayoutException e ) - { - throw new RuntimeException( e ); - } - } ); + return layouts.computeIfAbsent( remoteRepository, r -> + { + try + { + return repositoryLayoutProvider.newRepositoryLayout( session, remoteRepository ); + } + catch ( NoRepositoryLayoutException e ) + { + throw new RuntimeException( e ); + } + } ); } /** - * Caches prefixes instances for remote repository within one session. + * Caches prefixes instances for remote repository. */ - @SuppressWarnings( "unchecked" ) - private Node cacheNode( RepositorySystemSession session, - Path basedir, + private Node cacheNode( Path basedir, RemoteRepository remoteRepository ) { - return ( (ConcurrentHashMap) session.getData() - .computeIfAbsent( NODE_CACHE_KEY, ConcurrentHashMap::new ) ) - .computeIfAbsent( remoteRepository.getId(), r -> loadRepositoryPrefixes( basedir, remoteRepository ) ); + return prefixes.computeIfAbsent( remoteRepository, r -> loadRepositoryPrefixes( basedir, remoteRepository ) ); } /** @@ -219,7 +209,7 @@ public Result acceptMetadata( RemoteRepository remoteRepository, Metadata metada private Result acceptPrefix( RemoteRepository remoteRepository, String path ) { - Node root = cacheNode( session, basedir, remoteRepository ); + Node root = cacheNode( basedir, remoteRepository ); if ( NOT_PRESENT_NODE == root ) { return NOT_PRESENT_RESULT; diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java index 984de212d..c6ccc985c 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessor.java @@ -42,6 +42,8 @@ import org.eclipse.aether.transfer.ChecksumFailureException; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.artifact.ArtifactIdUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; @@ -53,14 +55,14 @@ *

    * Configuration keys: *

      - *
    • {@code aether.artifactResolver.postProcessor.trusted-checksums.checksumAlgorithms} - Comma separated + *
    • {@code aether.artifactResolver.postProcessor.trustedChecksums.checksumAlgorithms} - Comma separated * list of {@link ChecksumAlgorithmFactory} names to use (default "SHA-1").
    • - *
    • {@code aether.artifactResolver.postProcessor.trusted-checksums.failIfMissing} - To fail if artifact + *
    • {@code aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing} - To fail if artifact * being validated is missing a trusted checksum (default {@code false}).
    • - *
    • {@code aether.artifactResolver.postProcessor.trusted-checksums.snapshots} - Should snapshot artifacts be + *
    • {@code aether.artifactResolver.postProcessor.trustedChecksums.snapshots} - Should snapshot artifacts be * handled (validated or recorded). Snapshots are by "best practice" in-house produced, hence should be trusted * (default {@code false}).
    • - *
    • {@code aether.artifactResolver.postProcessor.trusted-checksums.record} - If this value set to {@code true}, + *
    • {@code aether.artifactResolver.postProcessor.trustedChecksums.record} - If this value set to {@code true}, * this component with not validate but "record" encountered artifact checksums instead * (default {@code false}).
    • *
    @@ -78,7 +80,7 @@ public final class TrustedChecksumsArtifactResolverPostProcessor extends ArtifactResolverPostProcessorSupport { - public static final String NAME = "trusted-checksums"; + public static final String NAME = "trustedChecksums"; private static final String CONF_NAME_CHECKSUM_ALGORITHMS = "checksumAlgorithms"; @@ -93,6 +95,8 @@ public final class TrustedChecksumsArtifactResolverPostProcessor private static final String CHECKSUM_ALGORITHMS_CACHE_KEY = TrustedChecksumsArtifactResolverPostProcessor.class.getName() + ".checksumAlgorithms"; + private static final Logger LOGGER = LoggerFactory.getLogger( TrustedChecksumsArtifactResolverPostProcessor.class ); + private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; private final Map trustedChecksumsSources; @@ -161,20 +165,26 @@ private void recordArtifactChecksums( RepositorySystemSession session, for ( TrustedChecksumsSource trustedChecksumsSource : trustedChecksumsSources.values() ) { - try ( TrustedChecksumsSource.Writer writer = trustedChecksumsSource - .getTrustedArtifactChecksumsWriter( session ) ) + TrustedChecksumsSource.Writer writer = trustedChecksumsSource + .getTrustedArtifactChecksumsWriter( session ); + if ( writer != null ) { - if ( writer != null ) + try { writer.addTrustedArtifactChecksums( artifact, artifactRepository, checksumAlgorithmFactories, calculatedChecksums ); } + catch ( IOException e ) + { + throw new UncheckedIOException( "Could not write required checksums for " + + artifact.getFile(), e ); + } } } } catch ( IOException e ) { - throw new UncheckedIOException( "Could not calculate amd write required checksums for " + throw new UncheckedIOException( "Could not calculate required checksums for " + artifact.getFile(), e ); } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/DefaultSyncContextFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/DefaultSyncContextFactory.java index d7f6bd6c7..9fbc5f669 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/DefaultSyncContextFactory.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/DefaultSyncContextFactory.java @@ -23,27 +23,15 @@ import javax.inject.Named; import javax.inject.Singleton; -import java.util.HashMap; -import java.util.Map; - import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.SyncContext; -import org.eclipse.aether.internal.impl.synccontext.named.NameMapper; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapter; -import org.eclipse.aether.internal.impl.synccontext.named.providers.DiscriminatingNameMapperProvider; -import org.eclipse.aether.internal.impl.synccontext.named.providers.FileGAVNameMapperProvider; -import org.eclipse.aether.internal.impl.synccontext.named.providers.FileHashingGAVNameMapperProvider; -import org.eclipse.aether.internal.impl.synccontext.named.providers.GAVNameMapperProvider; -import org.eclipse.aether.internal.impl.synccontext.named.providers.StaticNameMapperProvider; -import org.eclipse.aether.named.NamedLockFactory; -import org.eclipse.aether.named.providers.FileLockNamedLockFactory; -import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory; -import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory; -import org.eclipse.aether.named.providers.NoopNamedLockFactory; +import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector; +import org.eclipse.aether.internal.impl.synccontext.named.ParameterizedNamedLockFactorySelector; import org.eclipse.aether.spi.locator.Service; import org.eclipse.aether.spi.locator.ServiceLocator; import org.eclipse.aether.spi.synccontext.SyncContextFactory; -import org.eclipse.aether.util.ConfigUtils; import static java.util.Objects.requireNonNull; @@ -55,29 +43,18 @@ public final class DefaultSyncContextFactory implements SyncContextFactory, Service { - private static final String ADAPTER_KEY = DefaultSyncContextFactory.class.getName() + ".adapter"; - - private static final String NAME_MAPPER_KEY = "aether.syncContext.named.nameMapper"; - - private static final String DEFAULT_NAME_MAPPER_NAME = GAVNameMapperProvider.NAME; - - private static final String FACTORY_KEY = "aether.syncContext.named.factory"; - - private static final String DEFAULT_FACTORY_NAME = LocalReadWriteLockNamedLockFactory.NAME; - - private Map nameMappers; - - private Map namedLockFactories; + private NamedLockFactoryAdapter namedLockFactoryAdapter; /** * Constructor used with DI, where factories are injected and selected based on key. */ @Inject - public DefaultSyncContextFactory( final Map nameMappers, - final Map namedLockFactories ) + public DefaultSyncContextFactory( final RepositorySystemLifecycle repositorySystemLifecycle, + final NamedLockFactorySelector selector ) { - this.nameMappers = requireNonNull( nameMappers ); - this.namedLockFactories = requireNonNull( namedLockFactories ); + repositorySystemLifecycle.addOnSystemEndedHandler( this::shutDownAdapter ); + this.namedLockFactoryAdapter = + new NamedLockFactoryAdapter( selector.getSelectedNameMapper(), selector.getSelectedNamedLockFactory() ); } /** @@ -94,59 +71,24 @@ public DefaultSyncContextFactory() @Override public void initService( final ServiceLocator locator ) { - HashMap mappers = new HashMap<>(); - mappers.put( StaticNameMapperProvider.NAME, new StaticNameMapperProvider().get() ); - mappers.put( GAVNameMapperProvider.NAME, new GAVNameMapperProvider().get() ); - mappers.put( DiscriminatingNameMapperProvider.NAME, new DiscriminatingNameMapperProvider().get() ); - mappers.put( FileGAVNameMapperProvider.NAME, new FileGAVNameMapperProvider().get() ); - mappers.put( FileHashingGAVNameMapperProvider.NAME, new FileHashingGAVNameMapperProvider().get() ); - this.nameMappers = mappers; - - HashMap factories = new HashMap<>(); - factories.put( NoopNamedLockFactory.NAME, new NoopNamedLockFactory() ); - factories.put( LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory() ); - factories.put( LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory() ); - factories.put( FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory() ); - this.namedLockFactories = factories; + locator.getService( RepositorySystemLifecycle.class ).addOnSystemEndedHandler( this::shutDownAdapter ); + NamedLockFactorySelector selector = new ParameterizedNamedLockFactorySelector(); + this.namedLockFactoryAdapter = + new NamedLockFactoryAdapter( selector.getSelectedNameMapper(), selector.getSelectedNamedLockFactory() ); } @Override public SyncContext newInstance( final RepositorySystemSession session, final boolean shared ) { requireNonNull( session, "session cannot be null" ); - NamedLockFactoryAdapter adapter = getOrCreateSessionAdapter( session ); - return adapter.newInstance( session, shared ); - } - - private NamedLockFactoryAdapter getOrCreateSessionAdapter( final RepositorySystemSession session ) - { - return (NamedLockFactoryAdapter) session.getData().computeIfAbsent( ADAPTER_KEY, () -> - { - String nameMapperName = ConfigUtils.getString( session, DEFAULT_NAME_MAPPER_NAME, NAME_MAPPER_KEY ); - String namedLockFactoryName = ConfigUtils.getString( session, DEFAULT_FACTORY_NAME, FACTORY_KEY ); - NameMapper nameMapper = nameMappers.get( nameMapperName ); - if ( nameMapper == null ) - { - throw new IllegalArgumentException( "Unknown NameMapper name: " + nameMapperName - + ", known ones: " + nameMappers.keySet() ); - } - NamedLockFactory namedLockFactory = namedLockFactories.get( namedLockFactoryName ); - if ( namedLockFactory == null ) - { - throw new IllegalArgumentException( "Unknown NamedLockFactory name: " + namedLockFactoryName - + ", known ones: " + namedLockFactories.keySet() ); - } - session.addOnCloseHandler( this::shutDownSessionAdapter ); - return new NamedLockFactoryAdapter( nameMapper, namedLockFactory ); - } ); + return namedLockFactoryAdapter.newInstance( session, shared ); } - private void shutDownSessionAdapter( RepositorySystemSession session ) + private void shutDownAdapter() { - NamedLockFactoryAdapter adapter = (NamedLockFactoryAdapter) session.getData().get( ADAPTER_KEY ); - if ( adapter != null ) + if ( namedLockFactoryAdapter != null ) { - adapter.shutdown(); + namedLockFactoryAdapter.shutdown(); } } } diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactorySelector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactorySelector.java new file mode 100644 index 000000000..db9ceefb2 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactorySelector.java @@ -0,0 +1,41 @@ +package org.eclipse.aether.internal.impl.synccontext.named; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.eclipse.aether.named.NamedLockFactory; + +/** + * Selector for {@link NamedLockFactory} and {@link NameMapper} that selects and exposes selected ones. Essentially + * all the named locks configuration is here. Implementations may use different strategies to perform selection. + * + * @since 1.7.3 + */ +public interface NamedLockFactorySelector +{ + /** + * Returns the selected {@link NamedLockFactory}, never {@code null}. + */ + NamedLockFactory getSelectedNamedLockFactory(); + + /** + * Returns the selected {@link NameMapper}, never {@code null}. + */ + NameMapper getSelectedNameMapper(); +} \ No newline at end of file diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/ParameterizedNamedLockFactorySelector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/ParameterizedNamedLockFactorySelector.java new file mode 100644 index 000000000..9d13fa146 --- /dev/null +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/ParameterizedNamedLockFactorySelector.java @@ -0,0 +1,147 @@ +package org.eclipse.aether.internal.impl.synccontext.named; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.aether.internal.impl.synccontext.named.providers.DiscriminatingNameMapperProvider; +import org.eclipse.aether.internal.impl.synccontext.named.providers.FileGAVNameMapperProvider; +import org.eclipse.aether.internal.impl.synccontext.named.providers.FileHashingGAVNameMapperProvider; +import org.eclipse.aether.internal.impl.synccontext.named.providers.GAVNameMapperProvider; +import org.eclipse.aether.internal.impl.synccontext.named.providers.StaticNameMapperProvider; +import org.eclipse.aether.named.NamedLockFactory; +import org.eclipse.aether.named.providers.FileLockNamedLockFactory; +import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory; +import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory; +import org.eclipse.aether.named.providers.NoopNamedLockFactory; + +/** + * Parameterized selector implementation that selects based on injected parameters. + * + * @since 1.9.0 + */ +@Singleton +@Named +public final class ParameterizedNamedLockFactorySelector + implements NamedLockFactorySelector +{ + private static final String FACTORY_KEY = "aether.syncContext.named.factory"; + + private static final String NAME_MAPPER_KEY = "aether.syncContext.named.nameMapper"; + + private static final Map FACTORIES; + + private static final String DEFAULT_FACTORY = LocalReadWriteLockNamedLockFactory.NAME; + + private static final Map NAME_MAPPERS; + + private static final String DEFAULT_NAME_MAPPER = GAVNameMapperProvider.NAME; + + static + { + HashMap factories = new HashMap<>(); + factories.put( NoopNamedLockFactory.NAME, new NoopNamedLockFactory() ); + factories.put( LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory() ); + factories.put( LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory() ); + factories.put( FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory() ); + FACTORIES = factories; + + HashMap mappers = new HashMap<>(); + mappers.put( StaticNameMapperProvider.NAME, new StaticNameMapperProvider().get() ); + mappers.put( GAVNameMapperProvider.NAME, new GAVNameMapperProvider().get() ); + mappers.put( DiscriminatingNameMapperProvider.NAME, new DiscriminatingNameMapperProvider().get() ); + mappers.put( FileGAVNameMapperProvider.NAME, new FileGAVNameMapperProvider().get() ); + mappers.put( FileHashingGAVNameMapperProvider.NAME, new FileHashingGAVNameMapperProvider().get() ); + NAME_MAPPERS = mappers; + } + + private final NamedLockFactory namedLockFactory; + + private final NameMapper nameMapper; + + /** + * Default constructor for non Eclipse Sisu uses. + */ + public ParameterizedNamedLockFactorySelector() + { + this( FACTORIES, DEFAULT_FACTORY, NAME_MAPPERS, DEFAULT_NAME_MAPPER ); + } + + /** + * Constructor that uses Eclipse Sisu parameter injection. + */ + @SuppressWarnings( "checkstyle:LineLength" ) + @Inject + public ParameterizedNamedLockFactorySelector( final Map factories, + @Named( "${" + FACTORY_KEY + ":-" + DEFAULT_FACTORY + "}" ) final String selectedFactoryName, + final Map nameMappers, + @Named( "${" + NAME_MAPPER_KEY + ":-" + DEFAULT_NAME_MAPPER + "}" ) final String selectedMapperName ) + { + this.namedLockFactory = selectNamedLockFactory( factories, selectedFactoryName ); + this.nameMapper = selectNameMapper( nameMappers, selectedMapperName ); + } + + /** + * Returns the selected {@link NamedLockFactory}, never null. + */ + @Override + public NamedLockFactory getSelectedNamedLockFactory() + { + return namedLockFactory; + } + + /** + * Returns the selected {@link NameMapper}, never null. + */ + @Override + public NameMapper getSelectedNameMapper() + { + return nameMapper; + } + + private static NamedLockFactory selectNamedLockFactory( final Map factories, + final String factoryName ) + { + NamedLockFactory factory = factories.get( factoryName ); + if ( factory == null ) + { + throw new IllegalArgumentException( "Unknown NamedLockFactory name: " + factoryName + + ", known ones: " + factories.keySet() ); + } + return factory; + } + + private static NameMapper selectNameMapper( final Map nameMappers, + final String nameMapperName ) + { + NameMapper nameMapper = nameMappers.get( nameMapperName ); + if ( nameMapper == null ) + { + throw new IllegalArgumentException( "Unknown NameMapper name: " + nameMapperName + + ", known ones: " + nameMappers.keySet() ); + } + return nameMapper; + } +} \ No newline at end of file diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceTestSupport.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceTestSupport.java index 494f942f0..d67478ff8 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceTestSupport.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/FileTrustedChecksumsSourceTestSupport.java @@ -27,6 +27,8 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.impl.RepositorySystemLifecycle; +import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.internal.test.util.TestUtils; import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; @@ -54,6 +56,8 @@ public abstract class FileTrustedChecksumsSourceTestSupport private ChecksumAlgorithmFactory checksumAlgorithmFactory; + private RepositorySystemLifecycle repositorySystemLifecycle; + private FileTrustedChecksumsSourceSupport subject; private boolean checksumWritten; @@ -64,27 +68,24 @@ public void before() throws Exception session = TestUtils.newSession(); // populate local repository checksumAlgorithmFactory = new Sha1ChecksumAlgorithmFactory(); - subject = prepareSubject(); + repositorySystemLifecycle = new DefaultRepositorySystemLifecycle(); + subject = prepareSubject( repositorySystemLifecycle ); checksumWritten = false; - try ( DefaultRepositorySystemSession prepareSession = new DefaultRepositorySystemSession( session ) ) + DefaultRepositorySystemSession prepareSession = new DefaultRepositorySystemSession( session ); + enableSource( prepareSession ); + TrustedChecksumsSource.Writer writer = subject.getTrustedArtifactChecksumsWriter( prepareSession ); + if ( writer != null ) { - enableSource( prepareSession ); - try ( TrustedChecksumsSource.Writer writer = subject.getTrustedArtifactChecksumsWriter( prepareSession ) ) - { - if ( writer != null ) - { - HashMap checksums = new HashMap<>(); - checksums.put( checksumAlgorithmFactory.getName(), ARTIFACT_TRUSTED_CHECKSUM ); - writer.addTrustedArtifactChecksums( ARTIFACT_WITH_CHECKSUM, prepareSession.getLocalRepository(), - Collections.singletonList( checksumAlgorithmFactory ), checksums ); - checksumWritten = true; - } - } + HashMap checksums = new HashMap<>(); + checksums.put( checksumAlgorithmFactory.getName(), ARTIFACT_TRUSTED_CHECKSUM ); + writer.addTrustedArtifactChecksums( ARTIFACT_WITH_CHECKSUM, prepareSession.getLocalRepository(), + Collections.singletonList( checksumAlgorithmFactory ), checksums ); + checksumWritten = true; } } - protected abstract FileTrustedChecksumsSourceSupport prepareSubject(); + protected abstract FileTrustedChecksumsSourceSupport prepareSubject( RepositorySystemLifecycle lifecycle ); protected abstract void enableSource( DefaultRepositorySystemSession session ); diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java index 767276763..36095382c 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SparseDirectoryTrustedChecksumsSourceTest.java @@ -21,13 +21,14 @@ import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.DefaultFileProcessor; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; public class SparseDirectoryTrustedChecksumsSourceTest extends FileTrustedChecksumsSourceTestSupport { @Override - protected FileTrustedChecksumsSourceSupport prepareSubject() + protected FileTrustedChecksumsSourceSupport prepareSubject( RepositorySystemLifecycle lifecycle ) { return new SparseDirectoryTrustedChecksumsSource( new DefaultFileProcessor(), new DefaultLocalPathComposer() ); } @@ -35,6 +36,6 @@ protected FileTrustedChecksumsSourceSupport prepareSubject() @Override protected void enableSource( DefaultRepositorySystemSession session ) { - session.setConfigProperty( "aether.trustedChecksumsSource.sparse-directory", Boolean.TRUE.toString() ); + session.setConfigProperty( "aether.trustedChecksumsSource.sparseDirectory", Boolean.TRUE.toString() ); } } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java index 4d27f4efb..f3e08edad 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSourceTest.java @@ -20,19 +20,20 @@ */ import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.impl.RepositorySystemLifecycle; import org.eclipse.aether.internal.impl.DefaultLocalPathComposer; public class SummaryFileTrustedChecksumsSourceTest extends FileTrustedChecksumsSourceTestSupport { @Override - protected FileTrustedChecksumsSourceSupport prepareSubject() + protected FileTrustedChecksumsSourceSupport prepareSubject( RepositorySystemLifecycle lifecycle ) { - return new SummaryFileTrustedChecksumsSource( new DefaultLocalPathComposer() ); + return new SummaryFileTrustedChecksumsSource( new DefaultLocalPathComposer(), lifecycle ); } @Override protected void enableSource( DefaultRepositorySystemSession session ) { - session.setConfigProperty( "aether.trustedChecksumsSource.summary-file", Boolean.TRUE.toString() ); + session.setConfigProperty( "aether.trustedChecksumsSource.summaryFile", Boolean.TRUE.toString() ); } } diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java index 6f3ea1dcc..cd6aa1532 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/bf/BfDependencyCollectorTest.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.collection.CollectRequest; diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java index 8b8f79904..236d5c880 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/GroupIdRemoteRepositoryFilterSourceTest.java @@ -27,6 +27,7 @@ import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.internal.impl.DefaultRepositorySystemLifecycle; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; @@ -36,11 +37,14 @@ */ public class GroupIdRemoteRepositoryFilterSourceTest extends RemoteRepositoryFilterSourceTestSupport { + private GroupIdRemoteRepositoryFilterSource groupIdRemoteRepositoryFilterSource; + @Override protected GroupIdRemoteRepositoryFilterSource getRemoteRepositoryFilterSource( DefaultRepositorySystemSession session, RemoteRepository remoteRepository ) { - return new GroupIdRemoteRepositoryFilterSource(); + return groupIdRemoteRepositoryFilterSource = + new GroupIdRemoteRepositoryFilterSource( new DefaultRepositorySystemLifecycle() ); } @Override @@ -53,7 +57,8 @@ protected void enableSource( DefaultRepositorySystemSession session ) protected void allowArtifact( DefaultRepositorySystemSession session, RemoteRepository remoteRepository, Artifact artifact ) { - try ( DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession( session ) ) + DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession( session ); + try { Artifact resolvedArtifact = artifact.setFile( Files.createTempFile( "test", "tmp" ).toFile() ); ArtifactResult artifactResult = new ArtifactResult( new ArtifactRequest( resolvedArtifact, @@ -64,8 +69,7 @@ protected void allowArtifact( DefaultRepositorySystemSession session, RemoteRepo enableSource( newSession ); newSession.setConfigProperty( "aether.remoteRepositoryFilter." + GroupIdRemoteRepositoryFilterSource.NAME + ".record", Boolean.TRUE.toString() ); - getRemoteRepositoryFilterSource( newSession, remoteRepository ) - .postProcess( newSession, artifactResults ); + groupIdRemoteRepositoryFilterSource.postProcess( newSession, artifactResults ); } catch ( IOException e ) { diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessorTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessorTest.java index 06cd721c0..b07ded54f 100644 --- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessorTest.java +++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/resolution/TrustedChecksumsArtifactResolverPostProcessorTest.java @@ -103,7 +103,7 @@ public Collection getChecksumAlgorithmFactories() subject = new TrustedChecksumsArtifactResolverPostProcessor( selector, Collections.singletonMap( TRUSTED_SOURCE_NAME, this ) ); trustedChecksumsWriter = null; - session.setConfigProperty( "aether.artifactResolver.postProcessor.trusted-checksums", Boolean.TRUE.toString() ); + session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums", Boolean.TRUE.toString() ); } // -- TrustedChecksumsSource interface BEGIN @@ -163,7 +163,7 @@ public void haveNoChecksumPass() @Test public void haveNoChecksumFailIfMissingEnabledFail() { - session.setConfigProperty( "aether.artifactResolver.postProcessor.trusted-checksums.failIfMissing", + session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing", Boolean.TRUE.toString() ); ArtifactResult artifactResult = createArtifactResult( artifactWithoutTrustedChecksum ); assertThat( artifactResult.isResolved(), equalTo( true ) ); @@ -204,14 +204,8 @@ public void addTrustedArtifactChecksums( Artifact artifact, ArtifactRepository a { recordedChecksum.set( trustedArtifactChecksums.get( checksumAlgorithmFactory.getName() ) ); } - - @Override - public void close() - { - // nop - } }; - session.setConfigProperty( "aether.artifactResolver.postProcessor.trusted-checksums.record", + session.setConfigProperty( "aether.artifactResolver.postProcessor.trustedChecksums.record", Boolean.TRUE.toString() ); ArtifactResult artifactResult = createArtifactResult( artifactWithTrustedChecksum ); assertThat( artifactResult.isResolved(), equalTo( true ) ); diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/checksums/TrustedChecksumsSource.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/checksums/TrustedChecksumsSource.java index 797b8ba23..8c909ec56 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/checksums/TrustedChecksumsSource.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/checksums/TrustedChecksumsSource.java @@ -19,7 +19,6 @@ * under the License. */ -import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.Map; @@ -58,10 +57,9 @@ Map getTrustedArtifactChecksums( RepositorySystemSession session List checksumAlgorithmFactories ); /** - * A writer that is able to write/add trusted checksums to this implementation. Should be treated as a resource - * as underlying implementation may rely on being closed after not used anymore. + * A writer that is able to write/add trusted checksums to this implementation. */ - interface Writer extends Closeable + interface Writer { /** * Performs whatever implementation requires to "set" (write/add/append) given map of trusted checksums. diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index 9ecbfd2ce..76b24f511 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -22,11 +22,11 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix --- | --- | --- | --- | --- `aether.artifactResolver.snapshotNormalization` | boolean | It replaces the timestamped snapshot file name with a filename containing the `SNAPSHOT` qualifier only. This only affects resolving/retrieving artifacts but not uploading those. | `true` | no `aether.artifactResolver.simpleLrmInterop` | boolean | Enable interop with Simple LRM. Ignored when RRF used. | `false` | no -`aether.artifactResolver.postProcessor.trusted-checksums` | boolean | Enable `trusted-checksums` resolver post processor. | `false` | no -`aether.artifactResolver.postProcessor.trusted-checksums.checksumAlgorithms` | String | Comma-separated list of checksum algorithms with which `trusted-checksums` should operate (validate or record). | `"SHA-1"` | no -`aether.artifactResolver.postProcessor.trusted-checksums.failIfMissing` | boolean | Makes `trusted-checksums` fail validation if a trusted checksum for an artifact is missing. | `false` | no -`aether.artifactResolver.postProcessor.trusted-checksums.record` | boolean | Makes `trusted-checksums` calculate and record checksums. | `false` | no -`aether.artifactResolver.postProcessor.trusted-checksums.snapshots` | boolean | Enables or disables snapshot processing in `trusted-checksum` post processor. | `false` | no +`aether.artifactResolver.postProcessor.trustedChecksums` | boolean | Enable `trustedChecksums` resolver post processor. | `false` | no +`aether.artifactResolver.postProcessor.trustedChecksums.checksumAlgorithms` | String | Comma-separated list of checksum algorithms with which `trustedChecksums` should operate (validate or record). | `"SHA-1"` | no +`aether.artifactResolver.postProcessor.trustedChecksums.failIfMissing` | boolean | Makes `trustedChecksums` fail validation if a trusted checksum for an artifact is missing. | `false` | no +`aether.artifactResolver.postProcessor.trustedChecksums.record` | boolean | Makes `trustedChecksums` calculate and record checksums. | `false` | no +`aether.artifactResolver.postProcessor.trustedChecksums.snapshots` | boolean | Enables or disables snapshot processing in `trustedChecksums` post processor. | `false` | no `aether.checksums.omitChecksumsForExtensions` | String | Comma-separated list of extensions with leading dot (example `.asc`) that should have checksums omitted. These are applied to sub-artifacts only. Note: to achieve 1.7.x `aether.checksums.forSignature=true` behaviour, pass empty string as value for this property. | `.asc` | no `aether.checksums.algorithms` | String | Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded). Resolver by default supports following algorithms: `MD5`, `SHA-1`, `SHA-256` and `SHA-512`. New algorithms can be added by implementing `ChecksumAlgorithmFactory` component. | `"SHA-1,MD5"` | no `aether.conflictResolver.verbose` | boolean | Flag controlling the conflict resolver's verbose mode. | `false` | no @@ -86,12 +86,12 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix `aether.syncContext.named.discriminating.discriminator` | String | A discriminator name prefix identifying a Resolver instance. | `"sha1('${hostname:-localhost}:${maven.repo.local}')"` or `"sha1('')"` if generation fails | no `aether.syncContext.named.discriminating.hostname` | String | The hostname to be used with discriminating mapper. | Detected with `InetAddress.getLocalHost().getHostName()` | no `aether.syncContext.named.redisson.configFile` | String | Path to a Redisson configuration file in YAML format. Read [official documentation](https://github.com/redisson/redisson/wiki/2.-Configuration) for details. | none or `"${maven.conf}/maven-resolver-redisson.yaml"` if present | no -`aether.trustedChecksumsSource.sparse-directory` | boolean | Enable `sparse-directory` trusted checksum source. | `false` | no -`aether.trustedChecksumsSource.sparse-directory.basedir` | String | The basedir path for `sparse-directory` trusted checksum source. If relative, resolved against local repository root, if absolute, used as is. | `".checksums"` | no -`aether.trustedChecksumsSource.sparse-directory.originAware` | boolean | Is trusted checksum source origin aware (factors in Repository ID into path) or not. | `true` | no -`aether.trustedChecksumsSource.summary-file` | boolean | Enable `summary-file` trusted checksum source. | `false` | no -`aether.trustedChecksumsSource.summary-file.basedir` | String | The basedir path for `summary-file` trusted checksum source. If relative, resolved against local repository root, if absolute, used as is. | `".checksums"` | no -`aether.trustedChecksumsSource.summary-file.originAware` | boolean | Is trusted checksum source origin aware (factors in Repository ID into path) or not. | `true` | no +`aether.trustedChecksumsSource.sparseDirectory` | boolean | Enable `sparseDirectory` trusted checksum source. | `false` | no +`aether.trustedChecksumsSource.sparseDirectory.basedir` | String | The basedir path for `sparseDirectory` trusted checksum source. If relative, resolved against local repository root, if absolute, used as is. | `".checksums"` | no +`aether.trustedChecksumsSource.sparseDirectory.originAware` | boolean | Is trusted checksum source origin aware (factors in Repository ID into path) or not. | `true` | no +`aether.trustedChecksumsSource.summaryFile` | boolean | Enable `summaryFile` trusted checksum source. | `false` | no +`aether.trustedChecksumsSource.summaryFile.basedir` | String | The basedir path for `summaryFile` trusted checksum source. If relative, resolved against local repository root, if absolute, used as is. | `".checksums"` | no +`aether.trustedChecksumsSource.summaryFile.originAware` | boolean | Is trusted checksum source origin aware (factors in Repository ID into path) or not. | `true` | no `aether.updateCheckManager.sessionState` | String | Manages the session state, i.e. influences if the same download requests to artifacts/metadata will happen multiple times within the same RepositorySystemSession. If `"enabled"` will enable the session state. If `"bypass"` will enable bypassing (i.e. store all artifact ids/metadata ids which have been updates but not evaluating those). All other values lead to disabling the session state completely. | `"enabled"` | no All properties which have `yes` in the column `Supports Repo ID Suffix` can be optionally configured specifically for a repository id. In that case the configuration property needs to be suffixed with a period followed by the repository id of the repository to configure, e.g. `aether.connector.http.headers.central` for repository with id `central`.