Skip to content

Commit

Permalink
[MRESOLVER-512] ScopeManager (#445)
Browse files Browse the repository at this point in the history
In Resolver 1.x times, resolver was unaware of "resolution scope", to get the wanted resolution scope caller had to tweak and set up various nits and bits, like selectors, filters, and so on. It was easy to miss. Similarly, resolver had no "first class" type for "dependency scope" either, they were just string labels (that everyone knew HOW should behave, but was never codified) and its meaning and notion was sprinkled across several classes. Introducing new scope in these conditions (or alter selector to something that would have new scopes, like Maven4 plans) was nearly impossible.

The ScopeManager aims to solve these issues: it defines "resolution scope" and "dependency scope", interprets them, and allows caller to simply make a call to "resolve me main-runtime" resolution scope. No hoops and loops. Moreover, it is FASTER than Resolver 1.x was, that used always same selector (to build dirty graph), so potentially huge graph even if you needed just a bit of it, that was later chopped down to clean up the graph. ScopeManager automates selector selection/setup, and goes directly for result, in most cases the resulting tree is done in first pass.

Finally, and most importantly, ScopeManager allows to be "configured": by supplying the recipe for dependency and resolution scopes, hence, makes Resolver 2.x versatile, in a sense, it is not "this or that" anymore, it can obey Maven3 and Maven4 dependency scopes at the same time.

---

https://issues.apache.org/jira/browse/MRESOLVER-512
  • Loading branch information
cstamas authored Mar 27, 2024
1 parent 7d567a5 commit 39452f7
Show file tree
Hide file tree
Showing 55 changed files with 3,976 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.scope.ScopeManager;
import org.eclipse.aether.scope.SystemDependencyScope;
import org.eclipse.aether.transfer.TransferListener;

/**
Expand Down Expand Up @@ -192,8 +194,13 @@ public RepositoryCache getCache() {
}

@Override
public SystemScopeHandler getSystemScopeHandler() {
return getSession().getSystemScopeHandler();
public ScopeManager getScopeManager() {
return getSession().getScopeManager();
}

@Override
public SystemDependencyScope getSystemDependencyScope() {
return getSession().getSystemDependencyScope();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.scope.ScopeManager;
import org.eclipse.aether.scope.SystemDependencyScope;
import org.eclipse.aether.transfer.TransferListener;

import static java.util.Objects.requireNonNull;
Expand Down Expand Up @@ -124,7 +126,7 @@ public final class DefaultRepositorySystemSession implements RepositorySystemSes

private RepositoryCache cache;

private SystemScopeHandler systemScopeHandler;
private ScopeManager scopeManager;

private final Function<Runnable, Boolean> onSessionEndedRegistrar;

Expand Down Expand Up @@ -164,7 +166,6 @@ public DefaultRepositorySystemSession(Function<Runnable, Boolean> onSessionEnded
authenticationSelector = NullAuthenticationSelector.INSTANCE;
artifactTypeRegistry = NullArtifactTypeRegistry.INSTANCE;
data = new DefaultSessionData();
systemScopeHandler = SystemScopeHandler.LEGACY;
this.onSessionEndedRegistrar = requireNonNull(onSessionEndedRegistrar, "onSessionEndedRegistrar");
}

Expand Down Expand Up @@ -204,7 +205,7 @@ public DefaultRepositorySystemSession(RepositorySystemSession session) {
setDependencyGraphTransformer(session.getDependencyGraphTransformer());
setData(session.getData());
setCache(session.getCache());
setSystemScopeHandler(session.getSystemScopeHandler());
setScopeManager(session.getScopeManager());
this.onSessionEndedRegistrar = session::addOnSessionEndedHandler;
}

Expand Down Expand Up @@ -806,23 +807,32 @@ public DefaultRepositorySystemSession setCache(RepositoryCache cache) {
}

@Override
public SystemScopeHandler getSystemScopeHandler() {
return systemScopeHandler;
public ScopeManager getScopeManager() {
return scopeManager;
}

/**
* Sets the system scope handler, must not be {@code null}.
* Sets the scope manager, may be {@code null}.
*
* @param systemScopeHandler The system scope handler, cannot be {@code null}.
* @param scopeManager The scope manager, may be {@code null}.
* @return The session for chaining, never {@code null}.
* @since 2.0.0
*/
public DefaultRepositorySystemSession setSystemScopeHandler(SystemScopeHandler systemScopeHandler) {
public DefaultRepositorySystemSession setScopeManager(ScopeManager scopeManager) {
verifyStateForMutation();
this.systemScopeHandler = requireNonNull(systemScopeHandler);
this.scopeManager = scopeManager;
return this;
}

@Override
public SystemDependencyScope getSystemDependencyScope() {
if (scopeManager != null) {
return scopeManager.getSystemScope().orElse(null);
} else {
return SystemDependencyScope.LEGACY;
}
}

/**
* Registers onSessionEnded handler, if able to.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.scope.ScopeManager;
import org.eclipse.aether.scope.SystemDependencyScope;
import org.eclipse.aether.transfer.TransferListener;

/**
Expand Down Expand Up @@ -390,12 +392,12 @@ interface SessionBuilder {
SessionBuilder setCache(RepositoryCache cache);

/**
* Sets the system scope handler for session, may not be {@code null}.
* Sets the scope manager for session, may be {@code null}.
*
* @param systemScopeHandler The system scope handler, may not be {@code null}.
* @param scopeManager The scope manager, may be {@code null}.
* @return The session for chaining, never {@code null}.
*/
SessionBuilder setSystemScopeHandler(SystemScopeHandler systemScopeHandler);
SessionBuilder setScopeManager(ScopeManager scopeManager);

/**
* Adds on session ended handler to be immediately registered when this builder creates session.
Expand Down Expand Up @@ -752,12 +754,27 @@ interface SessionBuilder {
RepositoryCache getCache();

/**
* Returns the system scope handler, never {@code null}.
* Returns the scope manager to be used in this session, may be {@code null} if not set.
*
* @return The system scope handler, never {@code null}.
* @return The scope manager or {@code null} if not set.
* @since 2.0.0
*/
SystemScopeHandler getSystemScopeHandler();
ScopeManager getScopeManager();

/**
* Returns the system dependency scope.
* <p>
* Shorthand method for {@link #getScopeManager()#getSystemDependencyScope()}.
* <p>
* If {@link ScopeManager} is set, {@link #getScopeManager()} returns non-null value, the result of
* {@link ScopeManager#getSystemScope()} is returned (that may be {@code null}). If no {@link ScopeManager}
* if set, then {@link SystemDependencyScope#LEGACY} instance is returned, as lack of scope manager means that
* resolver operates in "legacy" mode (Maven3 compatible mode).
*
* @return The system dependency scope or {@code null} if no such scope.
* @since 2.0.0
*/
SystemDependencyScope getSystemDependencyScope();

/**
* Registers a handler to execute when this session closed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public final class ArtifactProperties {
*
* @deprecated since 2.0, the semantic carried by this property and the fact this property is coupled to Resolver
* 1.x "system" scope (that was delegated to consumer application) implies this property should not be used anymore,
* instead, the {@link org.eclipse.aether.SystemScopeHandler} exposed via method
* {@link RepositorySystemSession#getSystemScopeHandler()} should be used.
* instead, the {@link org.eclipse.aether.scope.ScopeManager} exposed via method
* {@link RepositorySystemSession#getScopeManager()} should be used.
*/
@Deprecated
public static final String LOCAL_PATH = "localPath";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.scope.ResolutionScope;

/**
* A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to
Expand All @@ -38,6 +39,7 @@
* @see RepositorySystem#collectDependencies(org.eclipse.aether.RepositorySystemSession, CollectRequest)
*/
public final class CollectRequest {
private ResolutionScope resolutionScope;

private Artifact rootArtifact;

Expand Down Expand Up @@ -100,6 +102,27 @@ public CollectRequest(
setRepositories(repositories);
}

/**
* Gets the wanted resolution scope. If set, the {@link org.eclipse.aether.scope.ScopeManager} has to be set on
* session as well, otherwise it is considered user error: misconfigured collect request.
*
* @since 2.0.0
* @return The wanted resolution scope.
*/
public ResolutionScope getResolutionScope() {
return resolutionScope;
}

/**
* Sets the wanted resolution scope. Usable only if {@link org.eclipse.aether.scope.ScopeManager} is used.
*
* @since 2.0.0
* @param resolutionScope The wanted resolution scope, may be {@code null} to "drive by yourself".
*/
public void setResolutionScope(ResolutionScope resolutionScope) {
this.resolutionScope = resolutionScope;
}

/**
* Gets the root artifact for the dependency graph.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.
*/
package org.eclipse.aether.scope;

/**
* Generic dependency scope.
*
* @since 2.0.0
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public interface DependencyScope {
/**
* The label.
*/
String getId();

/**
* Shorthand method of {@link #getId()#equals(Object)}.
*/
default boolean is(String label) {
return getId().equals(label);
}

/**
* Is it transitive scope?
*/
boolean isTransitive();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.
*/
package org.eclipse.aether.scope;

/**
* Generic resolution scope.
*
* @since 2.0.0
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public interface ResolutionScope {
/**
* The label.
*/
String getId();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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.
*/
package org.eclipse.aether.scope;

import java.util.Collection;
import java.util.Optional;

/**
* Scope manager.
*
* @since 2.0.0
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
*/
public interface ScopeManager {
/**
* The label.
*/
String getId();

/**
* Returns the "system" scope, if exists.
* <p>
* This is a special scope. In this scope case, Resolver should handle it specially, as it has no POM (so is
* always a leaf on graph), is not in any repository, but is actually hosted on host OS file system. On resolution
* resolver merely checks is file present or not.
*/
Optional<SystemDependencyScope> getSystemScope();

/**
* Returns a specific dependency scope by label.
* <p>
* Note: despite returns optional, this method may throw as well, if manager set in "strict" mode.
*/
Optional<DependencyScope> getDependencyScope(String id);

/**
* Returns the "universe" (all) of dependency scopes.
*/
Collection<DependencyScope> getDependencyScopeUniverse();

/**
* Returns a specific resolution scope by label.
* <p>
* Note: despite returns optional, this method may throw as well, if manager set in "strict" mode.
*/
Optional<ResolutionScope> getResolutionScope(String id);

/**
* Returns the "universe" (all) of resolution scopes.
*/
Collection<ResolutionScope> getResolutionScopeUniverse();
}
Loading

0 comments on commit 39452f7

Please sign in to comment.