You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2021/02/05 07:33:47 UTC

[GitHub] [netbeans] JaroslavTulach commented on a change in pull request #2731: Support for VM/app argument injection to (maven) run and debug project actions

JaroslavTulach commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r570750392



##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {

Review comment:
       I suggest to name this method just `empty()` or (a bit too verbose) `emptyProcessParameters()` to follow the convention of [Collections](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#emptyList()).

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {

Review comment:
       Is it really necessary to have such helper methods in here? It seems to just increase the cognitive load of everyone looking at this API.

##########
File path: java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
##########
@@ -119,10 +126,22 @@ public void finished(boolean success) {
                     notifyFinished(context, success);
                 }
             };
+            List<String> args = argsToStringList(launchArguments.get("args"));
+            List<String> vmArgs = argsToStringList(launchArguments.get("vmArgs"));
+            
+            List<Object> fixedLookupContents = new ArrayList<>(Arrays.asList(
+                toRun, ioContext, progress
+            ));
+            if (!(args.isEmpty() && vmArgs.isEmpty())) {
+                ExplicitProcessParameters.Builder bld = ExplicitProcessParameters.builder();
+                bld.priorityArgs(vmArgs);
+                bld.args(args);
+                bld.appendArgs(false);
+                fixedLookupContents.add(bld.build());

Review comment:
       It would be useful to describe how to influence Maven/Gradle and pass in the JVM as well as user arguments including the `executeWith` or `ActionProvider` invocation.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the priority argument list should not be altered.
+     */
+    public List<String> getPriorityArguments() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Convenience method that returns an empty list, if no priority arguments are present. Useful to build
+     * the final argument list.
+     * @return priority arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getPriorityArgumentsOrEmpty() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Instructs to replace arguments collected so far.
+     * @return true, if arguments collected should be discarded.
+     */
+    public boolean isArgReplacement() {
+        return !appendArgs;
+    }
+
+    /**
+     * Instructs to replace priority arguments collected so far.
+     * @return true, if priority arguments collected should be discarded.
+     */
+    public boolean isPriorityArgReplacement() {
+        return !appendPriorityArgs;
+    }
+    
+    public @NonNull List<String> getAllArguments(List<String> middle) {
+        List<String> a = new ArrayList<>();
+        if (priorityArguments != null) {
+            a.addAll(priorityArguments);
+        }
+        if (middle != null && !middle.isEmpty()) {
+            a.addAll(middle);
+        }
+        if (arguments != null) {
+            a.addAll(arguments);
+        }
+        return a;
+    }
+    
+    /**
+     * Returns the argument lists merged. Priority arguments (if any) are passed first, followed
+     * by {@code middle} (if any), then (normal) arguments. The method is a convenience to build
+     * a complete command line for the launcher + command + command arguments.
+     * @return combined arguments.
+     */
+    public @NonNull List<String> getAllArguments(@NullAllowed String... middle) {
+        return getAllArguments(middle == null ? Collections.emptyList() : Arrays.asList(middle));
+    }
+
+    /**
+     * Merges ExplicitProcessParameters instructions found in the Lookup. See {@link #buildExplicitParameters(java.util.Collection)}
+     * for more details.
+     * @param context context for the execution
+     * @return merged instructions
+     */
+    @NonNull
+    public static ExplicitProcessParameters buildExplicitParameters(Lookup context) {
+        return buildExplicitParameters(context.lookupAll(ExplicitProcessParameters.class));
+    }
+    
+    /**
+     * Merges individual instruction. 
+     * This method serves as a convenience and uniform ("standard") methods to merge argument lists for process execution. Should be used
+     * whenever a process (build, run, tool, ...) is executed. If the feature diverges, it should document how it processes the
+     * {@link ExplicitProcessParamters}. It is <b>strongly recommended</b> to support explicit parameters in order to allow for 
+     * customizations and automation.
+     * <p>
+     * Processes instructions in the order of {@link Builder#withRank(int)} and appearance. Whenever an item is flagged as
+     * a replacement, all arguments (priority arguments) collected to that point are discarded. Item's arguments (priority arguments)
+     * will become the only ones listed.
+     * <p>
+     * <i>Note:</i> if a replacement instruction and all the following (if any) have {@link #getArguments()} {@code null} (= no change), 
+     * the result will report <b>no change</b>. It is therefore possible to <b>discard all contributions</b> by appending a no-change replacement 
+     * last.
+     * 
+     * @param items individual instructions.
+     * @return combined instructions.
+     */
+    public static ExplicitProcessParameters buildExplicitParameters(Collection<? extends ExplicitProcessParameters> items) {
+        List<? extends ExplicitProcessParameters> all = new ArrayList<>(items);
+        Collections.sort(all, (a, b) -> {
+            ExplicitProcessParameters x;
+            int d = a.rank - b.rank;
+            if (d != 0) {
+                return d;
+            }
+            return all.indexOf(a) - all.indexOf(b);
+        });
+        Builder b = builder(); // .appendArgs(all.isEmpty());
+        for (ExplicitProcessParameters item : all) {
+            b.combine(item);
+        }
+        return b.build();
+    }
+    
+    public static Builder builder() {
+        return new Builder();
+    }
+    
+    /**
+     * Builds the {@link ExplicitProcessParameters} instance. The builder initially:
+     * <ul>
+     * <li><b>appends</b> priority arguments
+     * <li><b>replaces</b> (normal) arguments
+     * </ul>
+     * and the mode can be overriden for each group.
+     */
+    public final static class Builder {
+        private int rank = 0;
+        private List<String> priorityArguments = null;
+        private List<String> arguments = null;
+        private Boolean  appendArgs;
+        private Boolean  appendPriorityArgs;
+        
+        private void initArgs() {
+            if (arguments == null) {
+                arguments = new ArrayList<>();
+                /*
+                if (appendArgs == null) {
+                    appendArgs = false;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single argument. {@code null} is ignored.
+         * @param a argument
+         * @return the builder
+         */
+        public Builder arg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initArgs();
+            arguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            // init even if the list is empty.
+            initArgs();
+            args.forEach(this::arg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed String... args) {
+            if (args == null) {
+                return this;
+            }
+            return args(Arrays.asList(args));
+        }
+        
+        private void initPriorityArgs() {
+            if (priorityArguments == null) {
+                priorityArguments = new ArrayList<>();
+                /*
+                if (appendPriorityArgs == null) {
+                    appendPriorityArgs = true;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single priority argument. {@code null} is ignored.
+         * @param a priority argument
+         * @return the builder
+         */
+        public Builder priorityArg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initPriorityArgs();
+            priorityArguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder priorityArgs(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            initPriorityArgs();
+            args.forEach(this::priorityArg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder priorityArgs(@NullAllowed String... args) {
+            if (args == null) {
+                return this;
+            }
+            return priorityArgs(Arrays.asList(args));
+        }
+        
+        /**
+         * Changes the combining  mode for args. Setting to false instructs
+         * that all arguments that may precede should be discarded and the
+         * arguments provided by the built {@link ExplicitProcessParameters} are the only
+         * ones passed to the process.
+         * @param append true to append, false to replace
+         * @return the builder
+         */
+        public Builder appendArgs(boolean append) {
+            this.appendArgs = append;
+            return this;
+        }
+        
+        /**
+         * Changes the combining mode for priority args. Setting to false instructs
+         * that all arguments that may precede should be discarded and the
+         * priority arguments provided by the built {@link ExplicitProcessParameters} are the only
+         * ones passed to the process.
+         * @param append true to append, false to replace
+         * @return the builder
+         */
+        public Builder appendPriorityArgs(boolean append) {
+            this.appendPriorityArgs = append;
+            return this;
+        }
+
+        /**
+         * Defines a rank (position) for combining. The default rank is {@code 0}.
+         * @param rank rank of the instruction
+         * @return the builder
+         */
+        public Builder withRank(int rank) {

Review comment:
       Usually NetBeans API use "priority" and and not "rank", but I understand you have tough situation as "priority" is taken for "priority arguments" in this case.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>

Review comment:
       While "priority" and "ordinary" arguments are great abstractions, I suggest to explain what it means in context of Java. E.g. priority arguments are passed to the JVM, followed by classname and then followed by ordinary arguments.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the priority argument list should not be altered.
+     */
+    public List<String> getPriorityArguments() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Convenience method that returns an empty list, if no priority arguments are present. Useful to build
+     * the final argument list.
+     * @return priority arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getPriorityArgumentsOrEmpty() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Instructs to replace arguments collected so far.
+     * @return true, if arguments collected should be discarded.
+     */
+    public boolean isArgReplacement() {
+        return !appendArgs;
+    }
+
+    /**
+     * Instructs to replace priority arguments collected so far.
+     * @return true, if priority arguments collected should be discarded.
+     */
+    public boolean isPriorityArgReplacement() {
+        return !appendPriorityArgs;
+    }
+    
+    public @NonNull List<String> getAllArguments(List<String> middle) {
+        List<String> a = new ArrayList<>();
+        if (priorityArguments != null) {
+            a.addAll(priorityArguments);
+        }
+        if (middle != null && !middle.isEmpty()) {
+            a.addAll(middle);
+        }
+        if (arguments != null) {
+            a.addAll(arguments);
+        }
+        return a;
+    }
+    
+    /**
+     * Returns the argument lists merged. Priority arguments (if any) are passed first, followed
+     * by {@code middle} (if any), then (normal) arguments. The method is a convenience to build
+     * a complete command line for the launcher + command + command arguments.
+     * @return combined arguments.
+     */
+    public @NonNull List<String> getAllArguments(@NullAllowed String... middle) {
+        return getAllArguments(middle == null ? Collections.emptyList() : Arrays.asList(middle));
+    }
+
+    /**
+     * Merges ExplicitProcessParameters instructions found in the Lookup. See {@link #buildExplicitParameters(java.util.Collection)}
+     * for more details.
+     * @param context context for the execution
+     * @return merged instructions
+     */
+    @NonNull
+    public static ExplicitProcessParameters buildExplicitParameters(Lookup context) {
+        return buildExplicitParameters(context.lookupAll(ExplicitProcessParameters.class));
+    }
+    
+    /**
+     * Merges individual instruction. 
+     * This method serves as a convenience and uniform ("standard") methods to merge argument lists for process execution. Should be used
+     * whenever a process (build, run, tool, ...) is executed. If the feature diverges, it should document how it processes the
+     * {@link ExplicitProcessParamters}. It is <b>strongly recommended</b> to support explicit parameters in order to allow for 
+     * customizations and automation.
+     * <p>
+     * Processes instructions in the order of {@link Builder#withRank(int)} and appearance. Whenever an item is flagged as
+     * a replacement, all arguments (priority arguments) collected to that point are discarded. Item's arguments (priority arguments)
+     * will become the only ones listed.
+     * <p>
+     * <i>Note:</i> if a replacement instruction and all the following (if any) have {@link #getArguments()} {@code null} (= no change), 
+     * the result will report <b>no change</b>. It is therefore possible to <b>discard all contributions</b> by appending a no-change replacement 
+     * last.
+     * 
+     * @param items individual instructions.
+     * @return combined instructions.
+     */
+    public static ExplicitProcessParameters buildExplicitParameters(Collection<? extends ExplicitProcessParameters> items) {
+        List<? extends ExplicitProcessParameters> all = new ArrayList<>(items);
+        Collections.sort(all, (a, b) -> {
+            ExplicitProcessParameters x;
+            int d = a.rank - b.rank;
+            if (d != 0) {
+                return d;
+            }
+            return all.indexOf(a) - all.indexOf(b);

Review comment:
       Querying list that is just being re-ordered for actual position of elements in middle of re-ordering!? Adventurous!
   
   I guess you can return `0` - probably (I believe) the `sort` algorithm guarantees stability for equal elements.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>

Review comment:
       Please use `{@codesnippet}` tag as outlined at [codesnippet4javadoc readme](https://github.com/jtulach/codesnippet4javadoc/blob/master/README.md).

##########
File path: ide/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ExplicitProcessParametersTest.java
##########
@@ -0,0 +1,322 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author sdedic
+ */
+public class ExplicitProcessParametersTest extends NbTestCase {
+
+    public ExplicitProcessParametersTest(String name) {
+        super(name);
+    }
+    
+    List<String> existingVMArgs = new ArrayList<>(Arrays.asList(
+        "-Xmx100m"
+    ));
+    
+    List<String> existingAppArgs = new ArrayList<>(Arrays.asList(
+        "File1"
+    ));
+    
+    private void assertContains(List<String> args, String... items) {
+        for (String s : items) {
+            assertTrue("Must contain: " + s, args.stream().map(String::trim).filter(a -> s.equals(a)).findAny().isPresent());
+        }
+    }
+    
+    private void assertNotContains(List<String> args, String... items) {
+        for (String s : items) {
+            assertFalse("Must NOT contain: " + s, args.stream().map(String::trim).filter(a -> s.equals(a)).findAny().isPresent());
+        }
+    }
+    
+    /**
+     * Empty params, or params created w/o any content should have no effect when applied.
+     * 
+     * @throws Exception 
+     */
+    public void testEmptyExplicitParameters() throws Exception {
+        ExplicitProcessParameters empty = ExplicitProcessParameters.makeEmpty();
+        assertTrue(empty.isEmpty());
+        assertFalse(empty.isArgReplacement());
+        assertFalse(empty.isPriorityArgReplacement());
+
+        ExplicitProcessParameters empty2 = ExplicitProcessParameters.builder().build();
+        assertTrue(empty2.isEmpty());
+        assertFalse(empty2.isArgReplacement());
+        assertFalse(empty2.isPriorityArgReplacement());
+        
+        ExplicitProcessParameters base = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                build();
+        
+        ExplicitProcessParameters p = ExplicitProcessParameters.builder().
+                combine(base).
+                combine(empty).
+                build();
+        
+        ExplicitProcessParameters p2 = ExplicitProcessParameters.builder().
+                combine(base).
+                combine(empty).
+                build();
+        
+        assertEquals(existingVMArgs, p.getPriorityArguments());
+        assertEquals(existingAppArgs, p.getArguments());
+
+        assertEquals(existingVMArgs, p2.getPriorityArguments());
+        assertEquals(existingAppArgs, p2.getArguments());
+    }
+    
+    public void testSingleAddVMParams() throws Exception {
+        ExplicitProcessParameters extra = ExplicitProcessParameters.builder().
+                priorityArg("-Dfoo=bar").
+                build();
+        
+        assertFalse("No override requested", extra.isPriorityArgReplacement());
+        assertFalse("No arguments given", extra.isArgReplacement());
+        assertNull(extra.getArguments());
+
+        ExplicitProcessParameters p = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                combine(extra).
+                build();
+        
+        
+        assertContains(p.getPriorityArguments(), "-Xmx100m", "-Dfoo=bar");
+    }
+    
+    public void testSingleReplaceAppParams() throws Exception {
+        ExplicitProcessParameters extra = ExplicitProcessParameters.builder().
+                arg("avalanche").
+                build();
+        
+        ExplicitProcessParameters p = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                combine(extra).
+                build();
+
+        assertFalse("No override requested", extra.isPriorityArgReplacement());
+        assertTrue("Args must be replaced by default", extra.isArgReplacement());
+        assertNull(extra.getPriorityArguments());
+
+        
+        assertContains(p.getArguments(), "avalanche");
+        assertNotContains(p.getArguments(), "File1");
+    }
+    
+    public void testSingleDefaultLaunchAugmentation() throws Exception {
+        ExplicitProcessParameters extra = ExplicitProcessParameters.builder().
+                arg("avalanche").
+                priorityArg("-Dfoo=bar").
+                build();
+        
+        assertFalse("No prio override requested", extra.isPriorityArgReplacement());
+        assertTrue("Args must be replaced by default", extra.isArgReplacement());
+        assertNotNull(extra.getPriorityArguments());
+        assertNotNull(extra.getArguments());
+
+        ExplicitProcessParameters p = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                combine(extra).
+                build();
+        
+        assertContains(p.getPriorityArguments(), "-Xmx100m", "-Dfoo=bar");
+        assertContains(p.getArguments(), "avalanche");
+        assertNotContains(p.getArguments(), "File1");
+    }
+    
+    /**
+     * Checks that VM parmeters can be replaced.
+     * @throws Exception 
+     */
+    public void testReplacePriorityArgs() throws Exception {
+        ExplicitProcessParameters extra = ExplicitProcessParameters.builder().
+                priorityArg("-Dfoo=bar").
+                appendPriorityArgs(false).
+                build();
+        
+        ExplicitProcessParameters extra2 = ExplicitProcessParameters.builder().
+                priorityArg("-Dsun=shines").
+                build();
+
+        assertTrue("Must replace priority args", extra.isPriorityArgReplacement());
+        assertFalse("No arguments were specified", extra.isArgReplacement());
+        assertNull(extra.getArguments());
+
+        
+        ExplicitProcessParameters p = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                combine(extra).
+                build();
+        
+        assertContains(p.getPriorityArguments(), "-Dfoo=bar");
+        assertNotContains(p.getPriorityArguments(), "-Xmx100m");
+        
+        ExplicitProcessParameters p2 = ExplicitProcessParameters.builder().
+                priorityArgs(existingVMArgs).
+                args(existingAppArgs).
+                combine(
+                    ExplicitProcessParameters.buildExplicitParameters(Arrays.asList(extra, extra2))
+                ).
+                build();
+        
+        assertContains(p2.getPriorityArguments(), "-Dfoo=bar", "-Dsun=shines");
+        assertNotContains(p2.getPriorityArguments(), "-Xmx100m");
+    }
+    
+    public void testAppendNormalArgs() throws Exception {
+        ExplicitProcessParameters extra = ExplicitProcessParameters.builder().

Review comment:
       Some of these tests could be referenced with the `{@codesnippet}` tag in the documentation. 

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.

Review comment:
       I would welcome an example showing what this is good for here:
   
   _When working with Maven project use ..following code..  to achieve ..._

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the priority argument list should not be altered.
+     */
+    public List<String> getPriorityArguments() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Convenience method that returns an empty list, if no priority arguments are present. Useful to build
+     * the final argument list.
+     * @return priority arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getPriorityArgumentsOrEmpty() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Instructs to replace arguments collected so far.
+     * @return true, if arguments collected should be discarded.
+     */
+    public boolean isArgReplacement() {
+        return !appendArgs;
+    }
+
+    /**
+     * Instructs to replace priority arguments collected so far.
+     * @return true, if priority arguments collected should be discarded.
+     */
+    public boolean isPriorityArgReplacement() {
+        return !appendPriorityArgs;
+    }
+    
+    public @NonNull List<String> getAllArguments(List<String> middle) {

Review comment:
       Another helper method. This time without Javadoc.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.

Review comment:
       It would be nice to show code snippet how to do it properly.

##########
File path: java/maven/src/org/netbeans/modules/maven/execute/defaultActionMappings.xml
##########
@@ -117,7 +117,10 @@
             <goal>org.codehaus.mojo:exec-maven-plugin:3.0.0:exec</goal>
         </goals>
         <properties>
-            <exec.args>-classpath %classpath ${packageClassName}</exec.args>
+            <exec.vmArgs></exec.vmArgs>
+            <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>

Review comment:
       The `exec.vmArgs` and `exec.appArgs` shall be mentioned in `arch.xml` and as `<api group="property" type="export".../>`.

##########
File path: java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
##########
@@ -100,7 +103,35 @@
 
 /**
  * support for executing maven, externally on the command line.
+ * <b>Since 2/1.144</b>, the {@link LateBoundPrerequisitesChecker} registered in Maven projects by default supports 
+ * {@link ExplicitProcessParameters} API. The caller of the execute-type action can request to append or replace VM or user
+ * application parameters. The parameters recorded in the POM.xml or NetBeans action mappings are augmented according to that
+ * instructions:
+ * <ul>
+ * <li><b>priorityArgs</b> are mapped to VM arguments (precede main class name)
+ * <li><b>args</b> are mapped to user application arguments (after main class name)
+ * </ul>
+ * VM parameters injected by {@link StartupExtender} API are not affected by this feature. 
+ * <p>
+ * Example use:
+ * <code>
+ *   ActionProvider ap = ... ; // obtain ActionProvider from the project.

Review comment:
       If this could go into the documentation of `ExplicitProcessParameters` (even as a `non-normative` section), it would be far more visible. I am afraid Javadoc of this class isn't published in the official release documentation.

##########
File path: java/java.lsp.server/vscode/package.json
##########
@@ -108,6 +108,11 @@
 								],
 								"description": "The specified console to launch the program.",
 								"default": "internalConsole"
+							},
+							"args": {
+                                "type": "string",

Review comment:
       Space/tab issue? The indentation doesn't look right.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the priority argument list should not be altered.
+     */
+    public List<String> getPriorityArguments() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Convenience method that returns an empty list, if no priority arguments are present. Useful to build
+     * the final argument list.
+     * @return priority arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getPriorityArgumentsOrEmpty() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Instructs to replace arguments collected so far.
+     * @return true, if arguments collected should be discarded.
+     */
+    public boolean isArgReplacement() {
+        return !appendArgs;
+    }
+
+    /**
+     * Instructs to replace priority arguments collected so far.
+     * @return true, if priority arguments collected should be discarded.
+     */
+    public boolean isPriorityArgReplacement() {
+        return !appendPriorityArgs;
+    }
+    
+    public @NonNull List<String> getAllArguments(List<String> middle) {
+        List<String> a = new ArrayList<>();
+        if (priorityArguments != null) {
+            a.addAll(priorityArguments);
+        }
+        if (middle != null && !middle.isEmpty()) {
+            a.addAll(middle);
+        }
+        if (arguments != null) {
+            a.addAll(arguments);
+        }
+        return a;
+    }
+    
+    /**
+     * Returns the argument lists merged. Priority arguments (if any) are passed first, followed
+     * by {@code middle} (if any), then (normal) arguments. The method is a convenience to build
+     * a complete command line for the launcher + command + command arguments.
+     * @return combined arguments.
+     */
+    public @NonNull List<String> getAllArguments(@NullAllowed String... middle) {
+        return getAllArguments(middle == null ? Collections.emptyList() : Arrays.asList(middle));
+    }
+
+    /**
+     * Merges ExplicitProcessParameters instructions found in the Lookup. See {@link #buildExplicitParameters(java.util.Collection)}
+     * for more details.
+     * @param context context for the execution
+     * @return merged instructions
+     */
+    @NonNull
+    public static ExplicitProcessParameters buildExplicitParameters(Lookup context) {
+        return buildExplicitParameters(context.lookupAll(ExplicitProcessParameters.class));
+    }
+    
+    /**
+     * Merges individual instruction. 
+     * This method serves as a convenience and uniform ("standard") methods to merge argument lists for process execution. Should be used
+     * whenever a process (build, run, tool, ...) is executed. If the feature diverges, it should document how it processes the
+     * {@link ExplicitProcessParamters}. It is <b>strongly recommended</b> to support explicit parameters in order to allow for 
+     * customizations and automation.
+     * <p>
+     * Processes instructions in the order of {@link Builder#withRank(int)} and appearance. Whenever an item is flagged as
+     * a replacement, all arguments (priority arguments) collected to that point are discarded. Item's arguments (priority arguments)
+     * will become the only ones listed.
+     * <p>
+     * <i>Note:</i> if a replacement instruction and all the following (if any) have {@link #getArguments()} {@code null} (= no change), 
+     * the result will report <b>no change</b>. It is therefore possible to <b>discard all contributions</b> by appending a no-change replacement 
+     * last.
+     * 
+     * @param items individual instructions.
+     * @return combined instructions.
+     */
+    public static ExplicitProcessParameters buildExplicitParameters(Collection<? extends ExplicitProcessParameters> items) {
+        List<? extends ExplicitProcessParameters> all = new ArrayList<>(items);
+        Collections.sort(all, (a, b) -> {
+            ExplicitProcessParameters x;
+            int d = a.rank - b.rank;
+            if (d != 0) {
+                return d;
+            }
+            return all.indexOf(a) - all.indexOf(b);
+        });
+        Builder b = builder(); // .appendArgs(all.isEmpty());
+        for (ExplicitProcessParameters item : all) {
+            b.combine(item);
+        }
+        return b.build();
+    }
+    
+    public static Builder builder() {
+        return new Builder();
+    }
+    
+    /**
+     * Builds the {@link ExplicitProcessParameters} instance. The builder initially:
+     * <ul>
+     * <li><b>appends</b> priority arguments
+     * <li><b>replaces</b> (normal) arguments
+     * </ul>
+     * and the mode can be overriden for each group.
+     */
+    public final static class Builder {
+        private int rank = 0;
+        private List<String> priorityArguments = null;
+        private List<String> arguments = null;
+        private Boolean  appendArgs;
+        private Boolean  appendPriorityArgs;
+        
+        private void initArgs() {
+            if (arguments == null) {
+                arguments = new ArrayList<>();
+                /*
+                if (appendArgs == null) {
+                    appendArgs = false;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single argument. {@code null} is ignored.
+         * @param a argument
+         * @return the builder
+         */
+        public Builder arg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initArgs();
+            arguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            // init even if the list is empty.
+            initArgs();
+            args.forEach(this::arg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed String... args) {
+            if (args == null) {
+                return this;
+            }
+            return args(Arrays.asList(args));
+        }
+        
+        private void initPriorityArgs() {
+            if (priorityArguments == null) {
+                priorityArguments = new ArrayList<>();
+                /*
+                if (appendPriorityArgs == null) {
+                    appendPriorityArgs = true;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single priority argument. {@code null} is ignored.
+         * @param a priority argument
+         * @return the builder
+         */
+        public Builder priorityArg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initPriorityArgs();
+            priorityArguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder priorityArgs(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            initPriorityArgs();
+            args.forEach(this::priorityArg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}

Review comment:
       Appends!? There is also `appendXYZ` method next to here and that one doesn't append! That one is changing the replacing mode. Consider use "replace" terminology instead.

##########
File path: ide/extexecution.base/src/org/netbeans/api/extexecution/base/ExplicitProcessParameters.java
##########
@@ -0,0 +1,444 @@
+/*
+ * 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.netbeans.api.extexecution.base;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
+
+/**
+ * Allows to augment or replace process parameters for a single execution action.
+ * The class is intended to be used by launchers which build parameters based on some
+ * persistent configuration (project, workspace) to allow additions, or replacements
+ * for a single execution only.
+ * <p>
+ * It is <b>strongly recommended</b> for any feature that performs execution of a process to support {@link ExplicitProcessParameters},
+ * from a contextual {@link Lookup}, or at worst from {@link Lookup#getDefault()}. It will allow for future customizations and
+ * automation of the feature, enhancing the process launch for various environments, technologies etc.
+ * <p>
+ * <i>Note:</i> please refer also to {@code StartupExtender} API in the {@code extexecution} module, which contributes globally
+ * to priority arguments.
+ * <p>
+ * Two groups of parameters are recognized: {@link #getPriorityArguments()}, which should be passed
+ * first to the process (i.e. launcher parameters) and {@link #getArguments()} that represent the ordinary
+ * process arguments.
+ * <p>
+ * If the object is marked as {@link #isArgReplacement()}, the launcher implementor SHOULD replace all
+ * default or configured parameters with contents of this instruction. Both arguments and priorityArguments can have value {@code null}, which means "undefined": 
+ * in that case, the relevant group of configured parameters should not be affected.
+ * <p>
+ * Since these parameters are passed <b>externally<b>, there's an utility method, {@link #buildExplicitParameters(org.openide.util.Lookup)}
+ * that builds the explicit parameter instruction based on {@link Lookup} contents. The parameters are
+ * merged in the order of the {@link Builder#withRank configured rank} and appearance (in the sort priority order). 
+ * The default rank is {@code 0}, which allows both append or prepend parameters. If an item's 
+ * {@link ExplicitProcessParameters#isArgReplacement()} is true, all arguments collected so far are discarded.
+ * <p>
+ * If the combining algorithm is acceptable for the caller's purpose, the following pattern may be used to build the final
+ * command line:
+ * <code><pre>
+ *      ExplicitProcessParameters contextParams = ExplicitProcessParameters.buildExplicitParameters(runContext);
+ *      ExplicitProcessParameters combined = ExplicitProcessParameters.builder().
+ *              priorityArgs(extraArgs).
+ *              args(args).
+ *              combine(contextParams).build();
+ * </pre></code>
+ * This example will combine some args and extra args from project, or configuration with arguments passed from the
+ * {@code runContext} Lookup. 
+ * @author sdedic
+ * @since 1.16
+ */
+public final class ExplicitProcessParameters {
+    final int rank;
+    private final List<String>    priorityArguments;
+    private final List<String>    arguments;
+    private final boolean  appendArgs;
+    private final boolean  appendPriorityArgs;
+
+    private ExplicitProcessParameters(int rank, List<String> priorityArguments, 
+            List<String> arguments, boolean appendArgs, boolean appendPriorityArgs) {
+        this.rank = rank;
+        this.priorityArguments = priorityArguments == null ? null : Collections.unmodifiableList(priorityArguments);
+        this.arguments = arguments == null ? null : Collections.unmodifiableList(arguments);
+        this.appendArgs = appendArgs;
+        this.appendPriorityArgs = appendPriorityArgs;
+    }
+    
+    private static final ExplicitProcessParameters EMPTY = new ExplicitProcessParameters(0, null, null, true, true);
+    
+    /**
+     * Returns an empty instance of parameters that has no effect. DO NOT check for emptiness by
+     * equality or reference using the instance; use {@link #isEmpty()}.
+     * @return empty instance.
+     */
+    public static ExplicitProcessParameters makeEmpty() {
+        return EMPTY;
+    }
+    
+    /**
+     * Returns true, if the instance has no effect when {@link Builder#combine}d onto base parameters.
+     * @return true, if no effect is expected.
+     */
+    public boolean isEmpty() {
+        boolean change = false;
+        if (isArgReplacement() || isPriorityArgReplacement()) {
+            return false;
+        }
+        return (((arguments == null) || arguments.isEmpty()) && (priorityArguments == null || priorityArguments.isEmpty()));
+    }
+
+    /**
+     * Returns the arguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the argument list should not be altered.
+     */
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    /**
+     * Convenience method that returns an empty list, if no arguments are present. Useful to build
+     * the final argument list.
+     * @return arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getArgumentsOrEmpty() {
+        return arguments == null ? Collections.emptyList() : getArguments();
+    }
+
+    /**
+     * Returns the priorityarguments to be passed. Returns {@code null} if the object does not
+     * want to alter the argument list.
+     * @return arguments to be passed or {@code null} if the priority argument list should not be altered.
+     */
+    public List<String> getPriorityArguments() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Convenience method that returns an empty list, if no priority arguments are present. Useful to build
+     * the final argument list.
+     * @return priority arguments list to be passed, possibly empty (not null)
+     */
+    public @NonNull List<String> getPriorityArgumentsOrEmpty() {
+        return priorityArguments;
+    }
+    
+    /**
+     * Instructs to replace arguments collected so far.
+     * @return true, if arguments collected should be discarded.
+     */
+    public boolean isArgReplacement() {
+        return !appendArgs;
+    }
+
+    /**
+     * Instructs to replace priority arguments collected so far.
+     * @return true, if priority arguments collected should be discarded.
+     */
+    public boolean isPriorityArgReplacement() {
+        return !appendPriorityArgs;
+    }
+    
+    public @NonNull List<String> getAllArguments(List<String> middle) {
+        List<String> a = new ArrayList<>();
+        if (priorityArguments != null) {
+            a.addAll(priorityArguments);
+        }
+        if (middle != null && !middle.isEmpty()) {
+            a.addAll(middle);
+        }
+        if (arguments != null) {
+            a.addAll(arguments);
+        }
+        return a;
+    }
+    
+    /**
+     * Returns the argument lists merged. Priority arguments (if any) are passed first, followed
+     * by {@code middle} (if any), then (normal) arguments. The method is a convenience to build
+     * a complete command line for the launcher + command + command arguments.
+     * @return combined arguments.
+     */
+    public @NonNull List<String> getAllArguments(@NullAllowed String... middle) {
+        return getAllArguments(middle == null ? Collections.emptyList() : Arrays.asList(middle));
+    }
+
+    /**
+     * Merges ExplicitProcessParameters instructions found in the Lookup. See {@link #buildExplicitParameters(java.util.Collection)}
+     * for more details.
+     * @param context context for the execution
+     * @return merged instructions
+     */
+    @NonNull
+    public static ExplicitProcessParameters buildExplicitParameters(Lookup context) {
+        return buildExplicitParameters(context.lookupAll(ExplicitProcessParameters.class));
+    }
+    
+    /**
+     * Merges individual instruction. 
+     * This method serves as a convenience and uniform ("standard") methods to merge argument lists for process execution. Should be used
+     * whenever a process (build, run, tool, ...) is executed. If the feature diverges, it should document how it processes the
+     * {@link ExplicitProcessParamters}. It is <b>strongly recommended</b> to support explicit parameters in order to allow for 
+     * customizations and automation.
+     * <p>
+     * Processes instructions in the order of {@link Builder#withRank(int)} and appearance. Whenever an item is flagged as
+     * a replacement, all arguments (priority arguments) collected to that point are discarded. Item's arguments (priority arguments)
+     * will become the only ones listed.
+     * <p>
+     * <i>Note:</i> if a replacement instruction and all the following (if any) have {@link #getArguments()} {@code null} (= no change), 
+     * the result will report <b>no change</b>. It is therefore possible to <b>discard all contributions</b> by appending a no-change replacement 
+     * last.
+     * 
+     * @param items individual instructions.
+     * @return combined instructions.
+     */
+    public static ExplicitProcessParameters buildExplicitParameters(Collection<? extends ExplicitProcessParameters> items) {
+        List<? extends ExplicitProcessParameters> all = new ArrayList<>(items);
+        Collections.sort(all, (a, b) -> {
+            ExplicitProcessParameters x;
+            int d = a.rank - b.rank;
+            if (d != 0) {
+                return d;
+            }
+            return all.indexOf(a) - all.indexOf(b);
+        });
+        Builder b = builder(); // .appendArgs(all.isEmpty());
+        for (ExplicitProcessParameters item : all) {
+            b.combine(item);
+        }
+        return b.build();
+    }
+    
+    public static Builder builder() {
+        return new Builder();
+    }
+    
+    /**
+     * Builds the {@link ExplicitProcessParameters} instance. The builder initially:
+     * <ul>
+     * <li><b>appends</b> priority arguments
+     * <li><b>replaces</b> (normal) arguments
+     * </ul>
+     * and the mode can be overriden for each group.
+     */
+    public final static class Builder {
+        private int rank = 0;
+        private List<String> priorityArguments = null;
+        private List<String> arguments = null;
+        private Boolean  appendArgs;
+        private Boolean  appendPriorityArgs;
+        
+        private void initArgs() {
+            if (arguments == null) {
+                arguments = new ArrayList<>();
+                /*
+                if (appendArgs == null) {
+                    appendArgs = false;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single argument. {@code null} is ignored.
+         * @param a argument
+         * @return the builder
+         */
+        public Builder arg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initArgs();
+            arguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            // init even if the list is empty.
+            initArgs();
+            args.forEach(this::arg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder args(@NullAllowed String... args) {
+            if (args == null) {
+                return this;
+            }
+            return args(Arrays.asList(args));
+        }
+        
+        private void initPriorityArgs() {
+            if (priorityArguments == null) {
+                priorityArguments = new ArrayList<>();
+                /*
+                if (appendPriorityArgs == null) {
+                    appendPriorityArgs = true;
+                }
+                */
+            }
+        }
+        
+        /**
+         * Appends a single priority argument. {@code null} is ignored.
+         * @param a priority argument
+         * @return the builder
+         */
+        public Builder priorityArg(@NullAllowed String a) {
+            if (a == null) {
+                return this;
+            }
+            initPriorityArgs();
+            priorityArguments.add(a);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder priorityArgs(@NullAllowed List<String> args) {
+            if (args == null) {
+                return this;
+            }
+            initPriorityArgs();
+            args.forEach(this::priorityArg);
+            return this;
+        }
+
+        /**
+         * Appends arguments in the list. {@code null} is ignored as well as {@code null}
+         * items in the list.
+         * @param args argument list
+         * @return the builder
+         */
+        public Builder priorityArgs(@NullAllowed String... args) {
+            if (args == null) {
+                return this;
+            }
+            return priorityArgs(Arrays.asList(args));
+        }
+        
+        /**
+         * Changes the combining  mode for args. Setting to false instructs
+         * that all arguments that may precede should be discarded and the
+         * arguments provided by the built {@link ExplicitProcessParameters} are the only
+         * ones passed to the process.
+         * @param append true to append, false to replace
+         * @return the builder
+         */
+        public Builder appendArgs(boolean append) {
+            this.appendArgs = append;
+            return this;
+        }
+        
+        /**
+         * Changes the combining mode for priority args. Setting to false instructs
+         * that all arguments that may precede should be discarded and the
+         * priority arguments provided by the built {@link ExplicitProcessParameters} are the only
+         * ones passed to the process.
+         * @param append true to append, false to replace
+         * @return the builder
+         */
+        public Builder appendPriorityArgs(boolean append) {
+            this.appendPriorityArgs = append;
+            return this;
+        }
+
+        /**
+         * Defines a rank (position) for combining. The default rank is {@code 0}.

Review comment:
       Sorted up or down? Who's first?




----------------------------------------------------------------
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.

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



---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists