[
https://issues.apache.org/jira/browse/MRESOLVER-278?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17617747#comment-17617747
]
ASF GitHub Bot commented on MRESOLVER-278:
------------------------------------------
michael-o commented on code in PR #201:
URL: https://github.com/apache/maven-resolver/pull/201#discussion_r995792085
##########
maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java:
##########
@@ -245,4 +255,35 @@ public void close()
// nop
}
}
+
+ @SuppressWarnings( "unchecked" )
+ private void saveSessionRecordedLines( RepositorySystemSession session )
+ {
+ Map<Path, Set<String>> recordedLines = (Map<Path, Set<String>>)
session.getData().get( RECORDING_KEY );
+ if ( recordedLines == null || recordedLines.isEmpty() )
+ {
+ return;
+ }
+
+ ArrayList<Exception> exceptions = new ArrayList<>();
+ for ( Map.Entry<Path, Set<String>> entry : recordedLines.entrySet() )
+ {
+ Path file = entry.getKey();
+ Set<String> lines = entry.getValue();
+ if ( !lines.isEmpty() )
+ {
+ LOGGER.info( "Saving {} checksums to '{}'", lines.size(), file
);
+ try
+ {
+ Files.createDirectories( file.getParent() );
+ Files.write( file, lines );
Review Comment:
This is now truly ONE file per checksum algo?
##########
maven-resolver-api/src/test/java/org/eclipse/aether/DefaultRepositorySystemSessionTest.java:
##########
@@ -140,7 +140,10 @@ public void testCopyRepositorySystemSession() throws
Exception
for ( Method method : methods )
{
- assertEquals( method.getName(), method.invoke( session ) == null,
method.invoke( newSession ) == null );
+ if ( method.getParameterCount() == 0 )
+ {
+ assertEquals( method.getName(), method.invoke( session ) ==
null, method.invoke( newSession ) == null );
+ }
Review Comment:
This is an unrelated cleanup, no?
##########
maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java:
##########
@@ -873,4 +888,41 @@ public Collection<FileTransformer>
getTransformersForArtifact( Artifact artifact
}
}
+ private final CopyOnWriteArrayList<Consumer<RepositorySystemSession>>
onCloseHandlers
+ = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void addOnCloseHandler( Consumer<RepositorySystemSession> handler )
+ {
+ verifyStateForMutation();
+ requireNonNull( handler, "handler cannot be null" );
+ onCloseHandlers.add( 0, handler );
+ }
+
+ @Override
+ public boolean isClosed()
+ {
+ return closed.get();
+ }
+
+ @Override
+ public void close()
+ {
+ if ( closed.compareAndSet( false, true ) )
+ {
+ ArrayList<Exception> exceptions = new ArrayList<>();
+ for ( Consumer<RepositorySystemSession> onCloseHandler :
onCloseHandlers )
+ {
+ try
+ {
+ onCloseHandler.accept( this );
+ }
+ catch ( Exception e )
+ {
+ exceptions.add( e );
+ }
+ }
+ MultiRuntimeException.mayThrow( "session onClose handler
failures", exceptions );
Review Comment:
on-close
##########
maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java:
##########
@@ -873,4 +888,41 @@ public Collection<FileTransformer>
getTransformersForArtifact( Artifact artifact
}
}
+ private final CopyOnWriteArrayList<Consumer<RepositorySystemSession>>
onCloseHandlers
+ = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void addOnCloseHandler( Consumer<RepositorySystemSession> handler )
+ {
+ verifyStateForMutation();
+ requireNonNull( handler, "handler cannot be null" );
+ onCloseHandlers.add( 0, handler );
Review Comment:
Shouldn't this be just like with locks: FILO?
##########
maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java:
##########
@@ -201,41 +214,38 @@ private String calculateSummaryPath( boolean originAware,
return fileName + "." + checksumAlgorithmFactory.getFileExtension();
}
- /**
- * Note: this implementation will work only in single-thread (T1) model.
While not ideal, the "workaround" is
- * possible in both, Maven and Maven Daemon: force single threaded
execution model while "recording" (in mvn:
- * do not pass any {@code -T} CLI parameter, while for mvnd use {@code -1}
CLI parameter.
- *
- * TODO: this will need to be reworked for at least two reasons: a) avoid
duplicates in summary file and b)
- * support multi threaded builds (probably will need "on session close"
hook).
- */
private class SummaryFileWriter implements Writer
{
private final Path basedir;
private final boolean originAware;
- private SummaryFileWriter( Path basedir, boolean originAware )
+ private final ConcurrentHashMap<Path, Set<String>> recordedLines;
+
+ private SummaryFileWriter( Path basedir,
+ boolean originAware,
+ ConcurrentHashMap<Path, Set<String>>
recordedLines )
{
this.basedir = basedir;
this.originAware = originAware;
+ this.recordedLines = recordedLines;
}
@Override
public void addTrustedArtifactChecksums( Artifact artifact,
ArtifactRepository artifactRepository,
List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
- Map<String, String>
trustedArtifactChecksums ) throws IOException
+ Map<String, String>
trustedArtifactChecksums )
{
for ( ChecksumAlgorithmFactory checksumAlgorithmFactory :
checksumAlgorithmFactories )
{
String checksum = requireNonNull(
trustedArtifactChecksums.get(
checksumAlgorithmFactory.getName() ) );
- String summaryLine = ArtifactIdUtils.toId( artifact ) + " " +
checksum + "\n";
+ String summaryLine = ArtifactIdUtils.toId( artifact ) + " " +
checksum;
Path summaryPath = basedir.resolve(
calculateSummaryPath( originAware, artifactRepository,
checksumAlgorithmFactory ) );
- Files.createDirectories( summaryPath.getParent() );
- Files.write( summaryPath, summaryLine.getBytes(
StandardCharsets.UTF_8 ),
- StandardOpenOption.CREATE, StandardOpenOption.WRITE,
StandardOpenOption.APPEND );
+
+ recordedLines.computeIfAbsent( summaryPath, p ->
Collections.synchronizedSet( new TreeSet<>() ) )
+ .add( summaryLine );
Review Comment:
Do we really need it sorted or will insertion order just suffice?
##########
maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/DefaultSyncContextFactory.java:
##########
@@ -122,50 +114,39 @@ public void initService( final ServiceLocator locator )
public SyncContext newInstance( final RepositorySystemSession session,
final boolean shared )
{
requireNonNull( session, "session cannot be null" );
- NamedLockFactoryAdapter adapter =
- (NamedLockFactoryAdapter) session.getData().computeIfAbsent(
- ADAPTER_KEY,
- () -> createAdapter( session )
- );
+ NamedLockFactoryAdapter adapter = getOrCreateSessionAdapter( session );
return adapter.newInstance( session, shared );
}
- private NamedLockFactoryAdapter createAdapter( final
RepositorySystemSession session )
+ private NamedLockFactoryAdapter getOrCreateSessionAdapter( final
RepositorySystemSession session )
{
- 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 )
+ return (NamedLockFactoryAdapter) session.getData().computeIfAbsent(
ADAPTER_KEY, () ->
{
- throw new IllegalArgumentException( "Unknown NamedLockFactory
name: " + namedLockFactoryName
- + ", known ones: " + namedLockFactories.keySet() );
- }
- NamedLockFactoryAdapter adapter = new NamedLockFactoryAdapter(
nameMapper, namedLockFactory );
- createdAdapters.add( adapter );
- return adapter;
+ 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 );
+ } );
}
- @PreDestroy
- public void shutdown()
+ private void shutDownSessionAdapter( RepositorySystemSession session )
{
- LOGGER.debug( "Shutting down created adapters..." );
- createdAdapters.forEach( adapter ->
- {
- try
- {
- adapter.shutdown();
- }
- catch ( Exception e )
- {
- LOGGER.warn( "Could not shutdown: {}", adapter, e );
- }
- }
- );
+ NamedLockFactoryAdapter adapter = (NamedLockFactoryAdapter)
session.getData().get( ADAPTER_KEY );
+ if ( adapter != null )
+ {
+ adapter.shutdown();
+ }
Review Comment:
So the adapter showdown is now handled by the AutoCloseable for each?
##########
maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java:
##########
@@ -271,4 +272,57 @@
@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 TBD
+ */
+ void addOnCloseHandler( Consumer<RepositorySystemSession> 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 TBD
+ */
+ boolean isClosed();
+
+ /**
+ * Closes this session and possibly releases related resources by invoking
"on close" handlers added by
+ * {@link #addOnCloseHandler(Consumer<RepositorySystemSession>)} method.
This method may be invoked multiple times,
+ * but only first invocation will actually invoke handlers, subsequent
invocations will be no-op.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * The recommended pattern for "top level" sessions is the usual
try-with-resource:
+ *
+ * <pre> {@code
+ * try ( RepositorySystemSession session = create session... )
Review Comment:
This confuses me. In Maven the repo session is passed around like a whore,
how can this be achived in Maven? It can't, no?
##########
maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/SummaryFileTrustedChecksumsSource.java:
##########
@@ -201,41 +214,38 @@ private String calculateSummaryPath( boolean originAware,
return fileName + "." + checksumAlgorithmFactory.getFileExtension();
}
- /**
- * Note: this implementation will work only in single-thread (T1) model.
While not ideal, the "workaround" is
- * possible in both, Maven and Maven Daemon: force single threaded
execution model while "recording" (in mvn:
- * do not pass any {@code -T} CLI parameter, while for mvnd use {@code -1}
CLI parameter.
- *
- * TODO: this will need to be reworked for at least two reasons: a) avoid
duplicates in summary file and b)
- * support multi threaded builds (probably will need "on session close"
hook).
- */
private class SummaryFileWriter implements Writer
{
private final Path basedir;
private final boolean originAware;
- private SummaryFileWriter( Path basedir, boolean originAware )
+ private final ConcurrentHashMap<Path, Set<String>> recordedLines;
+
+ private SummaryFileWriter( Path basedir,
+ boolean originAware,
+ ConcurrentHashMap<Path, Set<String>>
recordedLines )
Review Comment:
Why do we need the semantics of a set?
> BREAKING: Make Session extend AutoCloseable (and introduce onCloseHandlers)
> ---------------------------------------------------------------------------
>
> Key: MRESOLVER-278
> URL: https://issues.apache.org/jira/browse/MRESOLVER-278
> Project: Maven Resolver
> Issue Type: New Feature
> Components: Resolver
> Reporter: Tamas Cservenak
> Assignee: Tamas Cservenak
> Priority: Major
> Fix For: 1.9.0
>
>
> So far, in (vanilla) Maven, the lifecycle of session was on par with
> lifecycle of SISU container, as Maven does something like this:
> * boot, create container
> * create session
> * work
> * destroy container
> * exit JVM
> So, Maven execution is 1 session 1 container, are on par.
> This is not true for cases where container (and resolver components) are
> reused across several sessions, like mvnd does. Also, current code on master
> (named locks adapter) uses {{@PreDestroy}} to shut down adapters, that is
> invoked when container is shut down, while the adapters are created
> per-session. This means that long-living mvnd daemons will shut down the
> unused adapter only when daemon itself is shut down, even is session for
> which adapter was created is long gone/done.
> While Maven has "session scoped" notion, resolver has not. Hence, simplest
> and cleanest solution is to make RepositorySystemSession have a method to
> "close", denoting that "this session is done". Also, if we can provide hooks
> for "onSessionClose", this resolves all the problems, as for example the
> adapter, that is created per session, could be now cleanly shut down at
> session end.
> One gotcha: this change implies a {*}breaking change for integrators of
> resolver{*}: integrator should make sure they close the session after they
> are done with it.
> Example changes needed for resolver users:
> [https://github.com/apache/maven/pull/822]
> The "pattern" to make use of this feature in resolver is following:
> * stuff something into session data, ideally using computeWhenAbsent
> * if absent, register a callback hook as well
> * in callback, get the stuffed thing from session and if present, do
> something with it
--
This message was sent by Atlassian Jira
(v8.20.10#820010)