View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util.graph.manager;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  
24  import org.eclipse.aether.collection.DependencyManager;
25  import org.eclipse.aether.graph.Exclusion;
26  import org.eclipse.aether.scope.ScopeManager;
27  import org.eclipse.aether.scope.SystemDependencyScope;
28  
29  /**
30   * A dependency manager that provides proper transitive dependency management for modern Maven usage.
31   *
32   * <h2>Overview</h2>
33   * <p>
34   * This manager implements proper "transitive dependency management" that works harmoniously
35   * with Maven's ModelBuilder. It produces more precise results regarding versions by respecting
36   * transitive management rules while allowing higher-level management to override lower-level rules.
37   * </p>
38   *
39   * <h2>Key Characteristics</h2>
40   * <ul>
41   * <li><strong>Transitive Management:</strong> {@code deriveUntil=Integer.MAX_VALUE}, {@code applyFrom=2}</li>
42   * <li><strong>ModelBuilder Friendly:</strong> Works in conjunction with, not against, ModelBuilder</li>
43   * <li><strong>Inheritance Aware:</strong> Special handling for scope and optional properties</li>
44   * <li><strong>Precise Versioning:</strong> Obeys transitive management unless managed at higher levels</li>
45   * </ul>
46   *
47   * <h2>Inheritance Handling</h2>
48   * <p>
49   * This manager provides special care for "scope" and "optional" properties that are subject
50   * to inheritance in the dependency graph during later graph transformation steps. These
51   * properties are only derived from the root to prevent interference with inheritance logic.
52   * </p>
53   *
54   * <h2>When to Use</h2>
55   * <p>
56   * This is the <strong>recommended manager for modern Maven projects</strong> that need proper
57   * transitive dependency management while maintaining compatibility with Maven's ModelBuilder.
58   * </p>
59   *
60   * <h2>Comparison with Other Managers</h2>
61   * <ul>
62   * <li>{@link ClassicDependencyManager}: Maven 2.x compatibility, limited transitive support</li>
63   * <li>{@link DefaultDependencyManager}: Aggressive but interferes with ModelBuilder</li>
64   * <li><strong>This manager:</strong> Modern, transitive, ModelBuilder-compatible (recommended)</li>
65   * </ul>
66   *
67   * @author Christian Schulte
68   * @since 1.4.0
69   * @see ClassicDependencyManager
70   * @see DefaultDependencyManager
71   */
72  public final class TransitiveDependencyManager extends AbstractDependencyManager {
73      /**
74       * Creates a new dependency manager without any management information.
75       *
76       * @deprecated Use {@link #TransitiveDependencyManager(ScopeManager)} instead to provide
77       *             application-specific scope management. This constructor uses legacy system
78       *             dependency scope handling.
79       */
80      @Deprecated
81      public TransitiveDependencyManager() {
82          this(null);
83      }
84  
85      /**
86       * Creates a new transitive dependency manager with ModelBuilder-compatible behavior.
87       * <p>
88       * This constructor initializes the manager with settings optimized for modern Maven usage:
89       * <ul>
90       * <li>deriveUntil = Integer.MAX_VALUE (collect management rules at all levels)</li>
91       * <li>applyFrom = 2 (apply management starting from depth 2, respecting ModelBuilder)</li>
92       * <li>Special inheritance handling for scope and optional properties</li>
93       * </ul>
94       *
95       * @param scopeManager application-specific scope manager for handling system dependencies,
96       *                     may be null to use legacy system dependency scope handling
97       */
98      public TransitiveDependencyManager(ScopeManager scopeManager) {
99          super(Integer.MAX_VALUE, 2, scopeManager);
100     }
101 
102     @SuppressWarnings("checkstyle:ParameterNumber")
103     private TransitiveDependencyManager(
104             ArrayList<AbstractDependencyManager> path,
105             int depth,
106             int deriveUntil,
107             int applyFrom,
108             MMap<Key, String> managedVersions,
109             MMap<Key, String> managedScopes,
110             MMap<Key, Boolean> managedOptionals,
111             MMap<Key, String> managedLocalPaths,
112             MMap<Key, Holder<Collection<Exclusion>>> managedExclusions,
113             SystemDependencyScope systemDependencyScope) {
114         super(
115                 path,
116                 depth,
117                 deriveUntil,
118                 applyFrom,
119                 managedVersions,
120                 managedScopes,
121                 managedOptionals,
122                 managedLocalPaths,
123                 managedExclusions,
124                 systemDependencyScope);
125     }
126 
127     @Override
128     protected DependencyManager newInstance(
129             MMap<Key, String> managedVersions,
130             MMap<Key, String> managedScopes,
131             MMap<Key, Boolean> managedOptionals,
132             MMap<Key, String> managedLocalPaths,
133             MMap<Key, Holder<Collection<Exclusion>>> managedExclusions) {
134         ArrayList<AbstractDependencyManager> path = new ArrayList<>(this.path);
135         path.add(this);
136         return new TransitiveDependencyManager(
137                 path,
138                 depth + 1,
139                 deriveUntil,
140                 applyFrom,
141                 managedVersions,
142                 managedScopes,
143                 managedOptionals,
144                 managedLocalPaths,
145                 managedExclusions,
146                 systemDependencyScope);
147     }
148 
149     /**
150      * Controls inheritance-based property derivation for scope and optional properties.
151      * <p>
152      * <strong>Why scope and optional are special:</strong> In dependency graphs, these two properties
153      * are subject to inheritance during graph transformation (which is outside ModelBuilder's scope).
154      * Therefore, scope and optional are derived only from the root to prevent interference with
155      * inheritance logic.
156      * </p>
157      * <p>
158      * <strong>The inheritance problem:</strong> If we managed scope/optional from sources below the root,
159      * we would mark nodes as "managed" in the dependency graph. The "managed" flag means "do not touch it,
160      * it is as it should be", which would prevent proper inheritance application during later graph
161      * transformation, causing nodes to end up with incorrect scope or optional states.
162      * </p>
163      * <p>
164      * <strong>Special case:</strong> The "system" scope has special handling due to its unique path requirements.
165      * </p>
166      *
167      * @return true only at depth 0 (root level) to ensure inheritance-based properties are only
168      *         derived from the root, false otherwise
169      */
170     @Override
171     protected boolean isInheritedDerived() {
172         return depth == 0;
173     }
174 }