You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@maven.apache.org by "Hiteshsai007 (via GitHub)" <gi...@apache.org> on 2026/10/18 18:44:07 UTC

[PR] Add filtered lifecycle execution API for IDE embedders [maven]

Hiteshsai007 opened a new pull request, #13612:
URL: https://github.com/apache/maven/pull/13612

   ### Description
   
   This PR addresses #13517 by adding a Maven 4 API for executing a filtered subset of a calculated lifecycle plan. It allows IDE embedders to select configured mojo executions without reproducing Maven internal execution loops.
   
   Issue: #13517
   
   **How it works:**
   I added a new `LifecycleExecutor` v4 service and a `Session.executeLifecycle(project, tasks, filter)` shortcut. Maven first calculates and configures the complete execution plan, then passes immutable v4 `MojoExecution` snapshots to the supplied predicate. The retained internal executions are submitted to `MojoExecutor` in a single batch so dependency resolution, lifecycle phase tracking, and execution order remain managed by Maven. The previous current project is restored after execution, including failure paths.
   
   **Why this is necessary:**
   Previously, IDE embedders such as m2e had to reproduce Maven internal `MojoExecutor` loops to execute selected lifecycle entries. This coupled embedders to mutable internal types and made them responsible for details such as dependency context creation and phase tracking. This API keeps the mutable execution objects inside Maven while exposing complete immutable metadata for filtering.
   
   **Testing:**
   * Added `DefaultLifecycleExecutorTest` to verify that Maven filters the calculated plan, executes the retained mojos as one unit, and restores the previous current project.
   * Successfully ran `mvn -pl impl/maven-core -am -DskipITs -Drat.skip=true -Dtest=DefaultLifecycleExecutorTest -Dsurefire.failIfNoSpecifiedTests=false test`. The 30-module focused reactor build passed with 1 test executed and no failures.
   * Formatting and Checkstyle completed successfully as part of the reactor build.
   
   ---
   
   <!--
   Please open pull requests against one of the following branches:
   
   - master — default target for all changes (new features + bug fixes).  
     If the change should also go to maven-4.0.x, request a backport in the PR.
   
   - maven-3.10.x — target for 3.x changes (new features + bug fixes).  
     If the change should also go to maven-3.9.x, request a backport in the PR.
     maven-3.9.x should receive only serious bug fixes, as it is approaching EOL.
   
   Backports are typically handled by maintainers; 
   however, after agreement you may open a separate backport PR to the target maintenance branch.
   -->
   
   Following this checklist to help us incorporate your
   contribution quickly and easily:
   
   - [x] Your pull request should address just one issue, without pulling in other changes.
   - [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
   - [x] Each commit in the pull request should have a meaningful subject line and body.
     Note that commits might be squashed by a maintainer on merge.
   - [x] Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied.
     This may not always be possible but is a best-practice.
   - [ ] Run `mvn verify` to make sure basic checks pass.
     A more thorough check will be performed on your pull request automatically.
   - [ ] You have run the [Core IT][core-its] successfully.
   
   If your pull request is about ~20 lines of code you do not need to sign an
   [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf) if you are unsure
   please ask on the developers list.
   
   To make clear that you license your contribution under
   the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0)
   you have to acknowledge this by using the following check-box.
   
   - [x] I hereby declare this contribution to be licenced under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0)
   - [ ] In any other case, please file an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf).
   
   [core-its]: https://maven.apache.org/core-its/core-it-suite/


-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13612:
URL: https://github.com/apache/maven/pull/13612#discussion_r4350385008


##########
api/maven-api-core/src/main/java/org/apache/maven/api/Session.java:
##########
@@ -217,6 +221,73 @@ default Map<String, String> getEffectiveProperties() {
     @Nonnull
     Collection<Listener> getListeners();
 
+    /**
+     * Calculates a lifecycle plan that can be inspected before execution.
+     *
+     * @param project the project for which to calculate the plan
+     * @param tasks lifecycle phases or plugin goals used to calculate the plan
+     * @return the lifecycle planning result
+     * @since 4.1.0
+     */
+    @Nonnull
+    default LifecyclePlanResult planLifecycle(@Nonnull Project project, @Nonnull List<String> tasks) {
+        return getService(LifecycleExecutor.class).plan(this, project, tasks);
+    }
+
+    /**
+     * Executes a previously calculated lifecycle plan.
+     *
+     * @param plan the lifecycle plan to execute
+     * @return the lifecycle execution result
+     * @since 4.1.0
+     */
+    @Nonnull
+    default LifecycleExecutionResult executeLifecycle(@Nonnull LifecyclePlan plan) {

Review Comment:
   ⚠️ **Missing `@throws` tags** — `executeLifecycle(LifecyclePlan)` delegates to `LifecycleExecutor.execute()` which declares `@throws LifecycleExecutorException` and `@throws IllegalArgumentException`. Both should be mirrored here (same applies to the other `executeLifecycle` overloads and the plan-and-execute variants).



##########
api/maven-api-core/src/main/java/org/apache/maven/api/Session.java:
##########
@@ -217,6 +221,73 @@ default Map<String, String> getEffectiveProperties() {
     @Nonnull
     Collection<Listener> getListeners();
 
+    /**
+     * Calculates a lifecycle plan that can be inspected before execution.
+     *
+     * @param project the project for which to calculate the plan
+     * @param tasks lifecycle phases or plugin goals used to calculate the plan
+     * @return the lifecycle planning result
+     * @since 4.1.0
+     */
+    @Nonnull
+    default LifecyclePlanResult planLifecycle(@Nonnull Project project, @Nonnull List<String> tasks) {

Review Comment:
   ⚠️ **Missing `@throws` tag** — `planLifecycle` delegates to `LifecycleExecutor.plan()` which documents `@throws LifecycleExecutorException if the lifecycle plan cannot be calculated`. The shortcut method should propagate that declaration:
   
   ```suggestion
        * @return the lifecycle planning result
        * @throws org.apache.maven.api.services.LifecycleExecutorException if the lifecycle plan cannot be calculated
        * @since 4.1.0
   ```



##########
api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutorException.java:
##########
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.maven.api.annotations.Experimental;
+
+/**
+ * Signals that lifecycle planning or execution failed.
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public class LifecycleExecutorException extends MavenException {
+
+    /**
+     * Creates an exception for a lifecycle planning or execution failure.
+     *
+     * @param message the detail message
+     * @param cause the underlying failure
+     */
+    public LifecycleExecutorException(String message, Throwable cause) {

Review Comment:
   💡 **Exception constructor coverage** — `LifecycleExecutorException` only exposes a `(String, Throwable)` constructor. Other v4 `MavenException` subclasses (e.g. `ProjectBuilderException`, `SettingsBuilderException`) also provide a message-only `(String)` constructor. Consider adding one for callers that wrap non-exception error conditions (e.g. plan calculation returning an empty result with a diagnostic message but no underlying exception).



##########
api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java:
##########
@@ -0,0 +1,91 @@
+/*
+ * 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 {

Review Comment:
   💡 **Naming collision note** — `org.apache.maven.lifecycle.LifecycleExecutor` already exists as a legacy (compat-layer) interface. Both live in the Maven classpath and have the same simple name, which will cause import ambiguity for embedders who access both layers. A brief Javadoc note pointing to the legacy counterpart (or a `@see` tag) would help orient readers. Example:
   
   ```suggestion
   /**
    * Calculates, inspects, and executes Maven lifecycle plans for a single project.
    *
    * <p>Note: this is the Maven API v4 lifecycle executor service. The legacy
    * {@code org.apache.maven.lifecycle.LifecycleExecutor} interface serves a
    * different role in the compat layer.
    *
    * @since 4.1.0
    */
   @Experimental
   public interface LifecycleExecutor extends Service {
   ```



-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "Hiteshsai007 (via GitHub)" <gi...@apache.org>.
Hiteshsai007 commented on code in PR #13612:
URL: https://github.com/apache/maven/pull/13612#discussion_r4310535410


##########
api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java:
##########
@@ -0,0 +1,59 @@
+/*
+ * 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.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 and executes Maven lifecycle plans.
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface LifecycleExecutor extends Service {

Review Comment:
   Renamed the public API to `LifecyclePlanExecutor` / `LifecyclePlanExecutionException` and the implementation to `DefaultLifecyclePlanExecutor`, avoiding the collision with the legacy lifecycle types. Addressed in 7fdf71d8f6.



##########
impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleExecutor.java:
##########
@@ -0,0 +1,106 @@
+/*
+ * 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.Project;
+import org.apache.maven.api.Service;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.services.LifecycleExecutionException;
+import org.apache.maven.api.services.LifecycleExecutor;
+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;
+    }
+
+    @Override
+    public void execute(
+            @Nonnull Session requestedSession,
+            @Nonnull Project project,
+            @Nonnull List<String> tasks,
+            @Nonnull Predicate<MojoExecution> filter) {
+        requireNonNull(requestedSession, "session");
+        requireNonNull(project, "project");
+        requireNonNull(tasks, "tasks").forEach(task -> requireNonNull(task, "task"));
+        requireNonNull(filter, "filter");
+
+        InternalMavenSession internalSession = InternalMavenSession.from(requestedSession);
+        MavenSession mavenSession = internalSession.getMavenSession();
+        MavenProject mavenProject =
+                cast(DefaultProject.class, project, "project").getProject();
+        MavenProject previousProject = mavenSession.getCurrentProject();
+
+        try {
+            mavenSession.setCurrentProject(mavenProject);
+            List<TaskSegment> taskSegments = taskSegmentCalculator.calculateTaskSegments(mavenSession, tasks);
+            List<org.apache.maven.plugin.MojoExecution> filteredPlan = new ArrayList<>();
+
+            for (TaskSegment taskSegment : taskSegments) {
+                MavenExecutionPlan executionPlan = executionPlanCalculator.calculateExecutionPlan(
+                        mavenSession, mavenProject, taskSegment.getTasks());
+                for (org.apache.maven.plugin.MojoExecution execution : executionPlan.getMojoExecutions()) {
+                    if (filter.test(new DefaultMojoExecution(internalSession, execution))) {
+                        filteredPlan.add(execution);
+                    }
+                }
+            }
+
+            mojoExecutor.execute(mavenSession, filteredPlan);
+        } catch (Exception e) {

Review Comment:
   Separated the three stages: plan calculation failures are wrapped as `LifecyclePlanExecutionException`, predicate evaluation runs outside those catch blocks so its runtime exceptions propagate unchanged, and only Maven lifecycle execution failures are wrapped during execution. I also added `doesNotWrapFilterFailures()` to lock in that behavior. Addressed in 7fdf71d8f6.



##########
impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultLifecyclePlanExecutorTest.java:
##########
@@ -0,0 +1,101 @@
+/*
+ * 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 java.util.List;
+
+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.LifecycleTask;
+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.plugin.MojoExecution;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class DefaultLifecycleExecutorTest {
+
+    @Test
+    void executesFilteredMojosAsOnePlanAndRestoresCurrentProject() throws Exception {

Review Comment:
   Added `restoresCurrentProjectOnExecutionFailure()`, which makes `MojoExecutor.execute()` fail and verifies that the prior current project is restored. The focused suite now has 3 passing tests. Addressed in 7fdf71d8f6.



-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #13612:
URL: https://github.com/apache/maven/pull/13612#issuecomment-6260814731

   <!-- license-declaration-check -->
   ### ⚠️ License declaration missing
   
   The pull request description must contain at least one of the following lines with a **checked** box (`[x]`):
   
   ```markdown
   - [ ] I hereby declare this contribution to be licensed under the [Apache License Version 2.0, January 2004](https://www.apache.org/licenses/LICENSE-2.0)
   - [ ] I hereby declare that I have filed an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf).
   ```
   
   This comment will be hidden automatically once the description is fixed.


-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13612:
URL: https://github.com/apache/maven/pull/13612#discussion_r4308281915


##########
api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java:
##########
@@ -0,0 +1,59 @@
+/*
+ * 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.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 and executes Maven lifecycle plans.
+ *
+ * @since 4.1.0
+ */
+@Experimental
+public interface LifecycleExecutor extends Service {

Review Comment:
   ⚠️ **Naming collision with internal `org.apache.maven.lifecycle.LifecycleExecutor`**
   
   The same simple name exists in two packages: `org.apache.maven.api.services.LifecycleExecutor` (this new type) and `org.apache.maven.lifecycle.LifecycleExecutor` (the long-standing internal interface that runs the full build). `DefaultLifecycleExecutor` in `impl/maven-core` already lives between both namespaces and imports from both.
   
   IDE embedders implementing against the Maven 4 API — the stated audience for this service — will face import conflicts as soon as they touch any lifecycle-adjacent internal APIs. This is new `@Experimental` API, so renaming is cheap now. `LifecyclePlanExecutor` or `FilteredLifecycleExecutor` would be unambiguous.



##########
impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultLifecycleExecutorTest.java:
##########
@@ -0,0 +1,101 @@
+/*
+ * 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 java.util.List;
+
+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.LifecycleTask;
+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.plugin.MojoExecution;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class DefaultLifecycleExecutorTest {
+
+    @Test
+    void executesFilteredMojosAsOnePlanAndRestoresCurrentProject() throws Exception {

Review Comment:
   💡 **Add a failure-path test to exercise the `finally` block**
   
   The current test only covers the success path. The `finally { mavenSession.setCurrentProject(previousProject); }` in `DefaultLifecycleExecutor` is the critical safety net for embedders — without it, a lifecycle execution failure would corrupt `currentProject` state in the session.
   
   Add a test where `mojoExecutor.execute()` throws to verify the restore still fires:
   
   ```java
   @Test
   void restoresCurrentProjectOnExecutionFailure() throws Exception {
       InternalMavenSession session = mock(InternalMavenSession.class);
       MavenSession mavenSession = mock(MavenSession.class);
       LifecycleTaskSegmentCalculator taskSegmentCalculator = mock(LifecycleTaskSegmentCalculator.class);
       LifecycleExecutionPlanCalculator executionPlanCalculator = mock(LifecycleExecutionPlanCalculator.class);
       MojoExecutor mojoExecutor = mock(MojoExecutor.class);
       MavenProject project = new MavenProject();
       MavenProject previousProject = new MavenProject();
       DefaultProject apiProject = mock(DefaultProject.class);
       MavenExecutionPlan plan = mock(MavenExecutionPlan.class);
       TaskSegment segment = new TaskSegment(false, new LifecycleTask("verify"));
   
       when(session.getMavenSession()).thenReturn(mavenSession);
       when(mavenSession.getCurrentProject()).thenReturn(previousProject);
       when(apiProject.getProject()).thenReturn(project);
       when(taskSegmentCalculator.calculateTaskSegments(mavenSession, List.of("verify")))
               .thenReturn(List.of(segment));
       when(executionPlanCalculator.calculateExecutionPlan(mavenSession, project, segment.getTasks()))
               .thenReturn(plan);
       when(plan.getMojoExecutions()).thenReturn(List.of());
       doThrow(new org.apache.maven.lifecycle.LifecycleExecutionException("boom"))
               .when(mojoExecutor).execute(eq(mavenSession), any());
   
       DefaultLifecycleExecutor executor =
               new DefaultLifecycleExecutor(taskSegmentCalculator, executionPlanCalculator, mojoExecutor);
   
       assertThrows(LifecycleExecutionException.class, () ->
               executor.execute(session, apiProject, List.of("verify"), e -> true));
   
       verify(mavenSession).setCurrentProject(same(previousProject));
   }
   ```



##########
impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleExecutor.java:
##########
@@ -0,0 +1,106 @@
+/*
+ * 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.Project;
+import org.apache.maven.api.Service;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.services.LifecycleExecutionException;
+import org.apache.maven.api.services.LifecycleExecutor;
+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;
+    }
+
+    @Override
+    public void execute(
+            @Nonnull Session requestedSession,
+            @Nonnull Project project,
+            @Nonnull List<String> tasks,
+            @Nonnull Predicate<MojoExecution> filter) {
+        requireNonNull(requestedSession, "session");
+        requireNonNull(project, "project");
+        requireNonNull(tasks, "tasks").forEach(task -> requireNonNull(task, "task"));
+        requireNonNull(filter, "filter");
+
+        InternalMavenSession internalSession = InternalMavenSession.from(requestedSession);
+        MavenSession mavenSession = internalSession.getMavenSession();
+        MavenProject mavenProject =
+                cast(DefaultProject.class, project, "project").getProject();
+        MavenProject previousProject = mavenSession.getCurrentProject();
+
+        try {
+            mavenSession.setCurrentProject(mavenProject);
+            List<TaskSegment> taskSegments = taskSegmentCalculator.calculateTaskSegments(mavenSession, tasks);
+            List<org.apache.maven.plugin.MojoExecution> filteredPlan = new ArrayList<>();
+
+            for (TaskSegment taskSegment : taskSegments) {
+                MavenExecutionPlan executionPlan = executionPlanCalculator.calculateExecutionPlan(
+                        mavenSession, mavenProject, taskSegment.getTasks());
+                for (org.apache.maven.plugin.MojoExecution execution : executionPlan.getMojoExecutions()) {
+                    if (filter.test(new DefaultMojoExecution(internalSession, execution))) {
+                        filteredPlan.add(execution);
+                    }
+                }
+            }
+
+            mojoExecutor.execute(mavenSession, filteredPlan);
+        } catch (Exception e) {

Review Comment:
   ⚠️ **`catch(Exception e)` conflates execution failures with buggy filter predicates**
   
   `mojoExecutor.execute()` throws `org.apache.maven.lifecycle.LifecycleExecutionException` (a checked exception representing a genuine plan execution failure). That should be caught and wrapped.
   
   But the filter lambda at line 93 — `filter.test(new DefaultMojoExecution(...))` — can also throw `RuntimeException` (e.g. a `NullPointerException` or `ClassCastException` from a buggy embedder predicate). Both currently land in `LifecycleExecutionException("Unable to execute filtered lifecycle plan", e)`. The caller cannot distinguish a real plan failure from a broken filter.
   
   Move the filter loop outside the try block, so only `mojoExecutor.execute()` and the plan calculation calls are caught:
   
   ```suggestion
           List<org.apache.maven.plugin.MojoExecution> filteredPlan = new ArrayList<>();
           try {
               mavenSession.setCurrentProject(mavenProject);
               List<TaskSegment> taskSegments = taskSegmentCalculator.calculateTaskSegments(mavenSession, tasks);
   
               for (TaskSegment taskSegment : taskSegments) {
                   MavenExecutionPlan executionPlan = executionPlanCalculator.calculateExecutionPlan(
                           mavenSession, mavenProject, taskSegment.getTasks());
                   for (org.apache.maven.plugin.MojoExecution execution : executionPlan.getMojoExecutions()) {
                       if (filter.test(new DefaultMojoExecution(internalSession, execution))) {
                           filteredPlan.add(execution);
                       }
                   }
               }
           } catch (Exception e) {
               throw new LifecycleExecutionException("Unable to calculate filtered lifecycle plan", e);
           }
           try {
               mojoExecutor.execute(mavenSession, filteredPlan);
           } catch (Exception e) {
               throw new LifecycleExecutionException("Unable to execute filtered lifecycle plan", e);
   ```



-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Add filtered lifecycle execution API for IDE embedders [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13612:
URL: https://github.com/apache/maven/pull/13612#discussion_r4391354594


##########
api/maven-api-core/src/main/java/org/apache/maven/api/services/LifecycleExecutor.java:
##########
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * <p>This is the Maven API v4 lifecycle executor service. The legacy
+ * {@code org.apache.maven.lifecycle.LifecycleExecutor} interface serves a different role in the compatibility layer.
+ *
+ * @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
+     * @throws LifecycleExecutorException if the lifecycle plan cannot be calculated
+     */
+    @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
+     * @throws LifecycleExecutorException if lifecycle execution fails
+     * @throws IllegalArgumentException if the plan was not produced by this service
+     */
+    @Nonnull
+    LifecycleExecutionResult execute(@Nonnull LifecycleExecutionRequest request);
+
+    @Nonnull
+    default LifecyclePlanResult plan(@Nonnull Session session, @Nonnull Project project, @Nonnull List<String> tasks) {
+        return plan(LifecyclePlanRequest.build(session, project, tasks));
+    }

Review Comment:
   ⚠️ **Missing Javadoc on all five default convenience methods** — `plan(Session, Project, List<String>)`, `execute(Session, LifecyclePlan)`, `execute(Session, LifecyclePlan, Predicate<>)`, and both `planAndExecute` overloads are the primary entry points for most callers (they avoid constructing request objects directly), yet they have no `@since`, no `@param`, no `@throws`, and no description at all.
   
   The abstract methods above them are well-documented; these defaults inherit nothing automatically. IDE users who discover the API through tab-completion see only the method signatures — they'll have no idea what `planAndExecute` does or that it can throw `LifecycleExecutorException`.
   
   At minimum, add `@since 4.1.0` and `@throws LifecycleExecutorException` to each. A one-line description and `@see` reference to the corresponding abstract method would also help.
   
   Example for the first default:
   ```suggestion
       /**
        * Calculates a lifecycle plan without executing it.
        *
        * @param session the current session
        * @param project the project for which to calculate the plan
        * @param tasks lifecycle phases or plugin goals used to calculate the plan
        * @return the lifecycle planning result
        * @throws LifecycleExecutorException if the lifecycle plan cannot be calculated
        * @since 4.1.0
        */
       @Nonnull
       default LifecyclePlanResult plan(@Nonnull Session session, @Nonnull Project project, @Nonnull List<String> tasks) {
           return plan(LifecyclePlanRequest.build(session, project, tasks));
       }
   ```



-- 
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: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org