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.apache.maven.api.services;
20
21 import java.nio.file.Path;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.stream.Stream;
27
28 import org.apache.maven.api.Language;
29 import org.apache.maven.api.ProducedArtifact;
30 import org.apache.maven.api.Project;
31 import org.apache.maven.api.ProjectScope;
32 import org.apache.maven.api.RemoteRepository;
33 import org.apache.maven.api.Service;
34 import org.apache.maven.api.Session;
35 import org.apache.maven.api.SourceRoot;
36 import org.apache.maven.api.annotations.Experimental;
37 import org.apache.maven.api.annotations.Nonnull;
38 import org.apache.maven.api.annotations.Nullable;
39
40 /**
41 * Interface to manage the project state and artifacts during the Maven build lifecycle.
42 * This service provides operations to:
43 * <ul>
44 * <li>Manage project artifacts (main and attached)</li>
45 * <li>Handle source roots and resources</li>
46 * <li>Access and modify project properties</li>
47 * <li>Manage repository configurations</li>
48 * <li>Handle project forking states</li>
49 * </ul>
50 *
51 * The service maintains the mutable state of projects as they progress through
52 * their build lifecycle, ensuring thread-safety and proper state management.
53 * All implementations must be thread-safe as they may be accessed concurrently
54 * during parallel builds.
55 *
56 * @since 4.0.0
57 * @see org.apache.maven.api.services.ProjectBuilder
58 * @see Project
59 */
60 @Experimental
61 public interface ProjectManager extends Service {
62 /**
63 * Returns the path to the built project artifact file, if the project has been built.
64 * This path is only available after the artifact has been produced during the build lifecycle.
65 *
66 * @param project the project to get the artifact path for
67 * @return an Optional containing the path to the built artifact if available,
68 * or empty if the artifact hasn't been built yet
69 */
70 @Nonnull
71 Optional<Path> getPath(@Nonnull Project project);
72
73 /**
74 * Returns an immutable collection of attached artifacts for the given project.
75 * Attached artifacts are secondary artifacts produced during the build (e.g., sources jar,
76 * javadoc jar, test jars). These artifacts are created and attached during specific
77 * lifecycle phases, so the collection contents depend on the build phase when this method
78 * is called.
79 *
80 * @param project the project to get attached artifacts for
81 * @return an immutable collection of attached artifacts, may be empty if no artifacts
82 * have been attached yet
83 * @throws IllegalArgumentException if the project is null
84 * @see #getAllArtifacts(Project)
85 */
86 @Nonnull
87 Collection<ProducedArtifact> getAttachedArtifacts(@Nonnull Project project);
88
89 /**
90 * Returns project's all artifacts as an immutable ordered collection. The collection contains:
91 * <ul>
92 * <li>The project's artifacts ({@link Project#getArtifacts()}):
93 * <ul>
94 * <li>The POM artifact (always present)</li>
95 * <li>The main project artifact (if applicable based on packaging)</li>
96 * </ul>
97 * </li>
98 * <li>All attached artifacts in the order they were attached</li>
99 * </ul>
100 * The contents depend on the current lifecycle phase when this method is called, as artifacts
101 * are typically attached during specific phases (e.g., sources jar during package phase).
102 *
103 * @param project the project to get artifacts for
104 * @return an immutable ordered collection of all project artifacts
105 * @see #getAttachedArtifacts(Project)
106 */
107 @Nonnull
108 Collection<ProducedArtifact> getAllArtifacts(@Nonnull Project project);
109
110 /**
111 * Attaches an artifact to the project using the given file path. The artifact type will be
112 * determined from the file extension. This method is thread-safe and ensures proper
113 * synchronization of the project's artifact state.
114 *
115 * @param session the current build session
116 * @param project the project to attach the artifact to
117 * @param path the path to the artifact file
118 */
119 default void attachArtifact(@Nonnull Session session, @Nonnull Project project, @Nonnull Path path) {
120 String name = path.getFileName().toString();
121 int dot = name.lastIndexOf('.');
122 String ext = dot >= 1 ? name.substring(dot + 1) : "";
123 ProducedArtifact artifact = session.createProducedArtifact(
124 project.getGroupId(), project.getArtifactId(), project.getVersion(), ext);
125 attachArtifact(project, artifact, path);
126 }
127
128 /**
129 * Attaches an artifact to the project with an explicitly specified type.
130 *
131 * @param session the current build session
132 * @param project the project to attach the artifact to
133 * @param type the type of the artifact (e.g., "jar", "war", "sources")
134 * @param path the path to the artifact file
135 * @see org.apache.maven.api.Type
136 */
137 default void attachArtifact(
138 @Nonnull Session session, @Nonnull Project project, @Nonnull String type, @Nonnull Path path) {
139 ProducedArtifact artifact = session.createProducedArtifact(
140 project.getGroupId(), project.getArtifactId(), project.getVersion(), null, null, type);
141 attachArtifact(project, artifact, path);
142 }
143
144 /**
145 * Attaches a produced artifact to the project at the specified path. This is the base method
146 * that the other attachArtifact methods delegate to.
147 *
148 * @param project the project to attach the artifact to
149 * @param artifact the produced artifact to attach
150 * @param path the path to the artifact file
151 */
152 void attachArtifact(@Nonnull Project project, @Nonnull ProducedArtifact artifact, @Nonnull Path path);
153
154 /**
155 * {@return all source root directories}, including the disabled ones, for all languages and scopes.
156 * For listing only the {@linkplain SourceRoot#enabled() enabled} source roots,
157 * the following code can be used:
158 *
159 * <pre>{@literal
160 * List<SourceRoot> enabledRoots = project.getSourceRoots()
161 * .stream().filter(SourceRoot::enabled).toList();
162 * }</pre>
163 *
164 * The iteration order is the order in which the sources are declared in the POM file.
165 *
166 * @param project the project for which to get the source roots
167 */
168 @Nonnull
169 Collection<SourceRoot> getSourceRoots(@Nonnull Project project);
170
171 /**
172 * {@return all enabled sources that provide files in the given language for the given scope}.
173 * If the given scope is {@code null}, then this method returns the enabled sources for all scopes.
174 * If the given language is {@code null}, then this method returns the enabled sources for all languages.
175 * An arbitrary number of source roots may exist for the same scope and language.
176 * It may be, for example, the case of a multi-versions project.
177 * The iteration order is the order in which the sources are declared in the POM file.
178 *
179 * @param project the project for which to get the enabled source roots
180 * @param scope the scope of the sources to return, or {@code null} for all scopes
181 * @param language the language of the sources to return, or {@code null} for all languages
182 */
183 @Nonnull
184 Stream<SourceRoot> getEnabledSourceRoots(
185 @Nonnull Project project, @Nullable ProjectScope scope, @Nullable Language language);
186
187 /**
188 * Adds the given source to the given project.
189 * If a source already exists for the given scope, language and directory,
190 * then the behavior depends on the {@code ProjectManager} implementation.
191 * It may do nothing or thrown {@linkplain IllegalArgumentException}.
192 *
193 * @param project the project to update
194 * @param source the source to add
195 * @throws IllegalArgumentException if this project manager rejects the given source because of conflict
196 *
197 * @see #getSourceRoots(Project)
198 */
199 void addSourceRoot(@Nonnull Project project, @Nonnull SourceRoot source);
200
201 /**
202 * Resolves and adds the given directory as a source with the given scope and language.
203 * First, this method resolves the given root against the project base directory, then normalizes the path.
204 * If no source already exists for the same scope, language and normalized directory,
205 * these arguments are added as a new {@link SourceRoot} element.
206 * Otherwise (i.e., in case of potential conflict), the behavior depends on the {@code ProjectManager}.
207 * The default implementation does nothing in the latter case.
208 *
209 * @param project the project to update
210 * @param scope scope (main or test) of the directory to add
211 * @param language language of the files contained in the directory to add
212 * @param directory the directory to add if not already present in the source
213 *
214 * @see #getEnabledSourceRoots(Project, ProjectScope, Language)
215 */
216 void addSourceRoot(
217 @Nonnull Project project, @Nonnull ProjectScope scope, @Nonnull Language language, @Nonnull Path directory);
218
219 /**
220 * Returns an immutable list of project remote repositories (directly specified or inherited).
221 * The repositories are ordered by declaration order, with inherited repositories appearing
222 * after directly specified ones.
223 *
224 * @param project the project
225 * @return ordered list of remote repositories
226 */
227 @Nonnull
228 List<RemoteRepository> getRemoteProjectRepositories(@Nonnull Project project);
229
230 /**
231 * Returns an immutable list of project plugin remote repositories (directly specified or inherited).
232 * The repositories are ordered by declaration order, with inherited repositories appearing
233 * after directly specified ones.
234 *
235 * @param project the project
236 * @return ordered list of remote repositories
237 */
238 @Nonnull
239 List<RemoteRepository> getRemotePluginRepositories(@Nonnull Project project);
240
241 /**
242 * {@return an immutable map of the project properties}.
243 *
244 * @param project the project for which to get the properties
245 *
246 * @see #setProperty(Project, String, String)
247 */
248 @Nonnull
249 Map<String, String> getProperties(@Nonnull Project project);
250
251 /**
252 * Set a given project property. Properties set through this method are only valid
253 * for the current build session and do not modify the underlying project model.
254 *
255 * @param project the project to modify
256 * @param key they property's key
257 * @param value the value or {@code null} to unset the property
258 */
259 void setProperty(@Nonnull Project project, @Nonnull String key, @Nullable String value);
260
261 /**
262 * Returns the original project being built when the input project is a forked project.
263 * During certain lifecycle phases, particularly for aggregator mojos, Maven may create
264 * a forked project (a copy of the original project) to execute a subset of the lifecycle.
265 * This method allows retrieving the original project that initiated the build.
266 *
267 * @param project the potentially forked project
268 * @return an Optional containing the original project if the input is a forked project,
269 * or an empty Optional if the input is already the original project
270 * @throws IllegalArgumentException if the project is null
271 */
272 @Nonnull
273 Optional<Project> getExecutionProject(@Nonnull Project project);
274 }