gnodet-bot commented on code in PR #13612: URL: https://github.com/apache/maven/pull/13612#discussion_r4350034738
########## impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleExecutor.java: ########## @@ -0,0 +1,162 @@ +/* + * 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.apache.maven.internal.impl; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import org.apache.maven.api.MojoExecution; +import org.apache.maven.api.Service; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.services.LifecycleExecutionRequest; +import org.apache.maven.api.services.LifecycleExecutionResult; +import org.apache.maven.api.services.LifecycleExecutor; +import org.apache.maven.api.services.LifecycleExecutorException; +import org.apache.maven.api.services.LifecyclePlanRequest; +import org.apache.maven.api.services.LifecyclePlanResult; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.MavenExecutionPlan; +import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator; +import org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator; +import org.apache.maven.lifecycle.internal.MojoExecutor; +import org.apache.maven.lifecycle.internal.TaskSegment; +import org.apache.maven.project.MavenProject; +import org.eclipse.sisu.Typed; + +import static java.util.Objects.requireNonNull; +import static org.apache.maven.internal.impl.CoreUtils.cast; + +@Named +@Typed({LifecycleExecutor.class, Service.class}) +@Singleton +public class DefaultLifecycleExecutor implements LifecycleExecutor { + + private final LifecycleTaskSegmentCalculator taskSegmentCalculator; + private final LifecycleExecutionPlanCalculator executionPlanCalculator; + private final MojoExecutor mojoExecutor; + + @Inject + public DefaultLifecycleExecutor( + LifecycleTaskSegmentCalculator taskSegmentCalculator, + LifecycleExecutionPlanCalculator executionPlanCalculator, + MojoExecutor mojoExecutor) { + this.taskSegmentCalculator = taskSegmentCalculator; + this.executionPlanCalculator = executionPlanCalculator; + this.mojoExecutor = mojoExecutor; + } + + @Nonnull + @Override + public LifecyclePlanResult plan(@Nonnull LifecyclePlanRequest request) { + requireNonNull(request, "request"); + InternalMavenSession internalSession = InternalMavenSession.from(request.getSession()); + MavenSession mavenSession = internalSession.getMavenSession(); + MavenProject mavenProject = + cast(DefaultProject.class, request.project(), "project").getProject(); + MavenProject previousProject = mavenSession.getCurrentProject(); + + mavenSession.setCurrentProject(mavenProject); + try { + List<org.apache.maven.plugin.MojoExecution> internalExecutions = + calculatePlan(mavenSession, mavenProject, request.tasks()); + List<MojoExecution> executions = internalExecutions.stream() + .map(execution -> (MojoExecution) new DefaultMojoExecution(internalSession, execution)) + .toList(); + DefaultLifecyclePlan plan = + new DefaultLifecyclePlan(request.project(), request.tasks(), executions, internalExecutions); + return new DefaultLifecyclePlanResult(request, plan); + } catch (Exception e) { + throw new LifecycleExecutorException("Unable to calculate lifecycle plan", e); + } finally { + mavenSession.setCurrentProject(previousProject); + } + } + + @Nonnull + @Override + public LifecycleExecutionResult execute(@Nonnull LifecycleExecutionRequest request) { + requireNonNull(request, "request"); + InternalMavenSession internalSession = InternalMavenSession.from(request.getSession()); + MavenSession mavenSession = internalSession.getMavenSession(); + DefaultLifecyclePlan plan = cast(DefaultLifecyclePlan.class, request.plan(), "plan"); Review Comment: 💡 **Note:** `cast(DefaultLifecyclePlan.class, request.plan(), "plan")` throws `IllegalArgumentException` if `request.plan()` is not an instance of `DefaultLifecyclePlan`. The error message will include the internal class name `org.apache.maven.internal.impl.DefaultLifecyclePlan` — an implementation detail surfacing through the public API. Consider adding `@throws IllegalArgumentException if plan was not produced by this service` to the Javadoc, or using a more descriptive error message that doesn't expose the internal type. ########## api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java: ########## @@ -0,0 +1,88 @@ +/* + * 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.apache.maven.api.services; + +import java.util.List; +import java.util.function.Predicate; + +import org.apache.maven.api.LifecyclePlan; +import org.apache.maven.api.MojoExecution; +import org.apache.maven.api.Project; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Calculates, inspects, and executes Maven lifecycle plans for a single project. + * + * @since 4.1.0 + */ +@Experimental +public interface LifecycleExecutor extends Service { + + /** + * Calculates a lifecycle plan without executing it. + * + * @param request the planning request + * @return the planning result + */ + @Nonnull + LifecyclePlanResult plan(@Nonnull LifecyclePlanRequest request); Review Comment: 💡 **Missing `@throws`:** `plan()` throws `LifecycleExecutorException` on failure, but this isn't documented in the Javadoc. Other v4 services document their exception types. Add: ```suggestion /** * Calculates a lifecycle plan without executing it. * * @param request the planning request * @return the planning result * @throws LifecycleExecutorException if the lifecycle plan cannot be calculated */ @Nonnull LifecyclePlanResult plan(@Nonnull LifecyclePlanRequest request); ``` ########## api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecyclePlanRequest.java: ########## @@ -0,0 +1,140 @@ +/* + * 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.apache.maven.api.services; + +import java.util.List; +import java.util.Objects; + +import org.apache.maven.api.Project; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A request to calculate a lifecycle plan for one project. + * + * @since 4.1.0 + */ +@Experimental +@Immutable +public interface LifecyclePlanRequest extends Request<Session> { + + /** {@return the project for which the lifecycle plan is calculated} */ + @Nonnull + Project project(); + + /** {@return the lifecycle phases and plugin goals used to calculate the plan} */ + @Nonnull + List<String> tasks(); + + @Nonnull + static Builder builder() { + return new Builder(); + } + + @Nonnull + static LifecyclePlanRequest build(@Nonnull Session session, @Nonnull Project project, @Nonnull List<String> tasks) { + return builder().session(session).project(project).tasks(tasks).build(); + } + + @NotThreadSafe + class Builder { + private Session session; + private RequestTrace trace; + private Project project; + private List<String> tasks; + + Builder() {} + + @Nonnull + public Builder session(@Nonnull Session session) { + this.session = session; + return this; + } + + @Nonnull + public Builder trace(@Nullable RequestTrace trace) { + this.trace = trace; + return this; + } + + @Nonnull + public Builder project(@Nonnull Project project) { + this.project = project; + return this; + } + + @Nonnull + public Builder tasks(@Nonnull List<String> tasks) { + this.tasks = tasks; + return this; + } + + @Nonnull + public LifecyclePlanRequest build() { + return new DefaultLifecyclePlanRequest(session, trace, project, tasks); + } + + private static final class DefaultLifecyclePlanRequest extends BaseRequest<Session> + implements LifecyclePlanRequest { + private final Project project; + private final List<String> tasks; + + private DefaultLifecyclePlanRequest( + Session session, RequestTrace trace, Project project, List<String> tasks) { + super(requireNonNull(session, "session cannot be null"), trace); + this.project = requireNonNull(project, "project cannot be null"); + this.tasks = List.copyOf(requireNonNull(tasks, "tasks cannot be null")); + this.tasks.forEach(task -> requireNonNull(task, "task cannot be null")); Review Comment: 💡 **Dead code:** `List.copyOf()` already throws `NullPointerException` for null elements (see Java 10 spec: "if any element is null"). The `forEach(requireNonNull(...))` on this line can never trigger — `List.copyOf()` on the previous line already rejected any null element. If you want the custom `"task cannot be null"` message, validate before calling `List.copyOf()`: ```suggestion Objects.requireNonNull(tasks, "tasks cannot be null"); tasks.forEach(task -> requireNonNull(task, "task cannot be null")); this.tasks = List.copyOf(tasks); ``` ########## api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java: ########## @@ -0,0 +1,88 @@ +/* + * 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.apache.maven.api.services; + +import java.util.List; +import java.util.function.Predicate; + +import org.apache.maven.api.LifecyclePlan; +import org.apache.maven.api.MojoExecution; +import org.apache.maven.api.Project; +import org.apache.maven.api.Service; +import org.apache.maven.api.Session; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Calculates, inspects, and executes Maven lifecycle plans for a single project. + * + * @since 4.1.0 + */ +@Experimental +public interface LifecycleExecutor extends Service { + + /** + * Calculates a lifecycle plan without executing it. + * + * @param request the planning request + * @return the planning result + */ + @Nonnull + LifecyclePlanResult plan(@Nonnull LifecyclePlanRequest request); + + /** + * Executes all or part of a previously calculated lifecycle plan. + * + * @param request the execution request + * @return the execution result + */ + @Nonnull + LifecycleExecutionResult execute(@Nonnull LifecycleExecutionRequest request); Review Comment: 💡 **Missing `@throws`:** `execute()` should also document `@throws LifecycleExecutorException if lifecycle execution fails`. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
