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/04 14:43:29 UTC

[GitHub] [netbeans] sdedic opened a new pull request #2731: Support for VM/app argument injection to (maven) run and debug project actions

sdedic opened a new pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731


   The need originated from LSP server, where the client wants to pass explicit arguments for executed application. As it turned out, this feature is generally handy in other scenarios, too.
   
   In addition, Maven project support provides all nice pieces just in a single `exec.args` property suitable directly for `exec:exec` plugin: this is a pain for custom setups, which could eventually accept VM arguments and app parameters from the IDE (LSP server) but cannot extract them from the single command line.
   
   The idea is to run the project action using `Lookups.executeWith()` and supply the lower layers with instructions how to modify the execution environment. @jtulach it may be also possible to use `context` parameter of `ProjectActionProvider.invokeAction()` ?
   
   Here's the summary of changes:
   - `ExplicitProcessParameters` immutable holder + builder API introduced in `extexecution` module. Serves as an instruction to modify process arguments, must be processed by the executor. 
   - Maven executor refactored "a bit". 
   - VM arguments and program arguments are now defined as properties; they are visible in Maven POM, too. `exec.args` now references the other properties.
   - `RunJarPanel` logic extracted to a helper class, unit tests written.
   - JAR packaging processing now combines POM or action mapping parameters with `ExplicitProcessArguments` to produce the result.
   
   During the course, I've fixed some tests in Maven that lacked test data.
   
   I also caught a race condition in Maven project implementation: if the project uses interpolated `packaging` attribute (i.e. `${packaging}`), that non-interpolated value will appear in the project and screws up the project Lookup contents: checkers for the real packaging will not be included, only afer 1st run. Fixed by forcing a reload after the real POM model is loaded by `MavenEmbedder`.
   
   In this PR, just Maven support jar packaging is changed. I'd like to apply similar change to Gradle projects, but would probably need help from @lkishalmi .


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178041



##########
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:
       Documented in 39417f0d8b

##########
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:
       Fixed in ef8b981300




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178145



##########
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:
       Fixed in ef8b981300 




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579038786



##########
File path: java/maven/arch.xml
##########
@@ -186,6 +193,20 @@
           to specify the default behavior of compile on save in Maven 
           projects.
       </api> 
+      <api category="devel" group="property" name="exec.vmArgs" type="export">
+          The plugin exports Java VM parameters to be used for application execution in <code>${exec.vmArgs}</code> 
+          property that can be used in action mappings or Maven pom.xml.
+      </api>
+      <api category="devel" group="property" name="exec.appArgs" type="export">
+          The plugin exports application parameters to be used for application execution in <code>${exec.appArgs}</code> 
+          property that can be used in action mappings or Maven pom.xml.
+      </api>
+      <api category="devel" group="lookup" name="ExplicitProcessParameters" type="export">

Review comment:
       Changed to `stable` in 9d7654ce75.




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-783683564


   .... and rebased again on fresh master to merge with the recent LSP fixes.


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


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

Posted by GitBox <gi...@apache.org>.
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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579038323



##########
File path: java/maven/test/unit/src/org/netbeans/modules/maven/runjar/MavenExecuteUtilsTest.java
##########
@@ -0,0 +1,191 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author sdedic
+ */
+public class MavenExecuteUtilsTest {

Review comment:
       Maven tests added to travis in 444f59bf72. I had to disable `CPExtenderTest` which was broken (not randomly failing) from NetBeans 8.2 (checked in the old `jet-main` build).
   




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178346



##########
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:
       Documented in 39417f0d8b




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572160075



##########
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:
       Given as non-normative example in 39417f0d8b 

##########
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:
       Removed in 39417f0d8b 




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572716276



##########
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:
       Fixed in 0d1d3ad




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579042029



##########
File path: java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
##########
@@ -0,0 +1,859 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.maven.NbMavenProjectImpl;
+import org.netbeans.modules.maven.api.customizer.ModelHandle2;
+import org.netbeans.modules.maven.customizer.RunJarPanel;
+import org.netbeans.modules.maven.execute.ActionToGoalUtils;
+import org.netbeans.modules.maven.execute.model.ActionToGoalMapping;
+import org.netbeans.modules.maven.execute.model.NetbeansActionMapping;
+import org.netbeans.modules.maven.runjar.RunJarStartupArgs;
+import org.netbeans.spi.project.ActionProvider;
+
+/**
+ *
+ * @author sdedic
+ */
+public final class MavenExecuteUtils {
+    /**
+     * Hint for the default PrereqqCheckeers that explicit parameters have been already processed.
+     * Will not be propagated to maven process.
+     */
+    public static final String RUN_EXPLICIT_PROCESSED = "NbIde.ExplicitParametersApplied"; // NOI18N
+    
+    /**
+     * Name of the property for VM arguments.
+     * @since 2.144
+     */
+    public static final String RUN_VM_PARAMS = "exec.vmArgs"; //NOI18N
+
+    /**
+     * Name of the property to pass main class. ${packageClassName} works as well.
+     * @since 2.144
+     */
+    public static final String RUN_MAIN_CLASS = "exec.mainClass"; //NOI18N
+
+    /**
+     * Name of the property for applicaiton arguments.
+     * @since 2.144
+     */
+    public static final String RUN_APP_PARAMS = "exec.appArgs"; //NOI18N
+
+    /**
+     * Name of the property that collects the entire command line.
+     */
+    public static final String RUN_PARAMS = "exec.args"; //NOI18N
+    
+    /**
+     * Name of the property for working directory passed to th exec plugin
+     */
+    public static final String RUN_WORKDIR = "exec.workingdir"; //NOI18N
+    
+    private static final String RUN_VM_PARAMS_TOKEN = "${" + RUN_VM_PARAMS + "}"; //NOI18N
+    private static final String RUN_APP_PARAMS_TOKEN = "${" + RUN_APP_PARAMS + "}"; //NOI18N
+    private static final String RUN_MAIN_CLASS_TOKEN = "${" + RUN_MAIN_CLASS + "}"; //NOI18N
+    static final String PACKAGE_CLASS_NAME_TOKEN = "${packageClassName}"; //NOI18N
+    
+    public static final String EXEC_ARGS_CLASSPATH_TOKEN = "-classpath %classpath"; // NOI18N
+    public static final String DEFAULT_EXEC_ARGS_CLASSPATH = EXEC_ARGS_CLASSPATH_TOKEN + " ${packageClassName}"; // NOI18N
+    static final String DEFAULT_DEBUG_PARAMS = "-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}"; //NOI18N
+    static final String DEFAULT_EXEC_ARGS_CLASSPATH2 =  "${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}"; // NOI18N
+
+    /**
+     * ID of the 'profile' action.
+     */
+    public static final String PROFILE_CMD = "profile"; // NOI18N
+    
+    /**
+     * A helper that can update action mappings based on changes
+     * made on the helper instance. Use {@link #createExecutionEnvHelper}
+     * to make an instance.
+     */
+    public final static class ExecutionEnvHelper {
+        private final ActionToGoalMapping goalMappings;
+        private final NbMavenProjectImpl project;
+        
+        private String oldAllParams;
+        private String oldVmParams;
+        private String oldAppParams;
+        private String oldWorkDir;
+        private String oldMainClass;
+        
+        private boolean currentRun;
+        private boolean currentDebug;
+        private boolean currentProfile;
+        
+        private String execParams;
+        private String vmParams;
+        private String appParams;
+        private String workDir;
+        private String mainClass;
+        
+        private NetbeansActionMapping run;
+        private NetbeansActionMapping debug;
+        private NetbeansActionMapping profile;
+
+        private boolean mergedConfig;
+        private boolean modified;
+
+        ExecutionEnvHelper(
+                NbMavenProjectImpl project,
+                NetbeansActionMapping run,
+                NetbeansActionMapping debug,
+                NetbeansActionMapping profile,
+                ActionToGoalMapping goalMappings) {
+            this.project = project;
+            this.goalMappings = goalMappings;
+            this.run = run;
+            this.debug = debug;
+            this.profile = profile;
+        }
+        
+        private String fallbackParams(String paramName, boolean stripDebug) {
+            String val = run != null ? run.getProperties().get(paramName) : null;
+            if (val == null && debug != null) {
+                val = debug.getProperties().get(paramName);
+                if (val != null && stripDebug) {
+                    val = String.join(" ", extractDebugJVMOptions(val));
+                }
+            }
+            if (val == null && profile != null) {
+                val = profile.getProperties().get(paramName);
+            }
+            return val == null ? "" : val.trim(); // NOI18N
+        }
+
+        private String appendIfNotEmpty(String a, String b) {
+            if (a == null || a.isEmpty()) {
+                return b;
+            }
+            if (b == null || b.isEmpty()) {
+                return a;
+            }
+            return a + " " + b;
+        }
+
+        public ActionToGoalMapping getGoalMappings() {
+            return goalMappings;
+        }
+
+        public boolean isModified() {
+            return modified;
+        }
+
+        public boolean isValid() {
+            return currentRun && currentDebug && currentProfile;
+        }
+
+        public boolean isCurrentRun() {
+            return currentRun;
+        }
+
+        public boolean isCurrentDebug() {
+            return currentDebug;
+        }
+
+        public boolean isCurrentProfile() {
+            return currentProfile;
+        }
+        
+        public void setMainClass(String mainClass) {
+            this.mainClass = mainClass;
+        }
+
+        public void setExecParams(String execParams) {
+            this.execParams = execParams;
+        }
+
+        public void setVmParams(String vmParams) {
+            this.vmParams = vmParams;
+        }
+
+        public void setAppParams(String appParams) {
+            this.appParams = appParams;
+        }
+
+        public NbMavenProjectImpl getProject() {
+            return project;
+        }
+
+        public String getWorkDir() {
+            return oldWorkDir;
+        }
+
+        public void setWorkDir(String workDir) {
+            this.workDir = workDir;
+        }
+
+        public String getMainClass() {
+            return mainClass;
+        }
+
+        public NetbeansActionMapping getRun() {
+            return run;
+        }
+
+        public NetbeansActionMapping getProfile() {
+            return profile;
+        }
+
+        public String getAllParams() {
+            return oldAllParams;
+        }
+
+        public String getVmParams() {
+            return vmParams;
+        }
+
+        public String getAppParams() {
+            return appParams;
+        }
+        
+        private NetbeansActionMapping getMapping(String a) {
+            NetbeansActionMapping m = ActionToGoalUtils.getDefaultMapping(a, project);
+            return m;
+        }
+        
+        /**
+         * Loads and parses values from the project's nbactions.xml
+         */
+        public void loadFromProject() {
+            NetbeansActionMapping m;
+            
+            if (run == null) {
+                run = getMapping(ActionProvider.COMMAND_RUN);
+            }
+            if (debug == null) {
+                debug = getMapping(ActionProvider.COMMAND_DEBUG);
+            }
+            if (profile == null) {
+                profile = getMapping(PROFILE_CMD);
+            }
+            
+            currentRun = checkNewMapping(run);
+            currentDebug = checkNewMapping(debug);
+            currentProfile = checkNewMapping(profile);
+            
+            oldWorkDir = fallbackParams(RUN_WORKDIR, false);
+            oldAllParams = fallbackParams(RUN_PARAMS, false);
+            oldVmParams = fallbackParams(RUN_VM_PARAMS, true);
+            oldAppParams = fallbackParams(RUN_APP_PARAMS, false);
+            oldMainClass = fallbackParams(RUN_MAIN_CLASS, false);
+            
+            mergedConfig = (oldVmParams.isEmpty() && oldAppParams.isEmpty() && oldMainClass.isEmpty());
+            
+            appendVMParamsFromOldParams();
+            addAppParamsFromOldParams();
+            loadMainClass();
+            
+            workDir = oldWorkDir;
+            vmParams = oldVmParams;
+            appParams = oldAppParams;
+            mainClass = oldMainClass;
+        }
+        
+        private String eraseTokens(String original, boolean withNewlines, String... tokens) {
+            StringBuilder sb = new StringBuilder();
+            for (String p : tokens) {
+                if (sb.length() > 0) {
+                    sb.append("|");
+                }
+                sb.append(Pattern.quote(p));
+                if (withNewlines) {
+                    sb.append("\\n?");
+                }
+            }
+            return original.replaceAll(sb.toString(), "").trim();
+        }
+        
+        private void appendVMParamsFromOldParams() {
+            String oldSplitVMParams = splitJVMParams(oldAllParams, true);
+            if (!oldSplitVMParams.isEmpty()) {
+                // try to get VM arguments out of all exec.args, but ignore -classpath added automatically, and
+                // exec.vmArgs present / added by default.
+                oldSplitVMParams = eraseTokens(oldSplitVMParams, true, "-classpath %classpath", RUN_VM_PARAMS_TOKEN);
+                oldVmParams = appendIfNotEmpty(oldVmParams, oldSplitVMParams);
+            }
+        }
+        
+        private void addAppParamsFromOldParams() {
+            String p = splitParams(oldAllParams);
+            if (!p.isEmpty()) {
+                p = eraseTokens(p, false, RUN_APP_PARAMS_TOKEN);
+                oldAppParams = appendIfNotEmpty(oldAppParams, p);
+            }
+        }
+        
+        private void loadMainClass() {
+            if (oldMainClass.trim().isEmpty()) {
+                oldMainClass = splitMainClass(oldAllParams);
+                // splitMainClass is never null
+            }
+            if (PACKAGE_CLASS_NAME_TOKEN.equals(oldMainClass) || RUN_MAIN_CLASS_TOKEN.equals(oldMainClass)) {
+                oldMainClass = "";
+            }
+        }
+
+        private boolean checkNewMapping(NetbeansActionMapping map) {
+            if (map == null || map.getGoals() == null) {
+                return false; //#164323
+            }
+            Iterator it = map.getGoals().iterator();
+            while (it.hasNext()) {
+                String goal = (String) it.next();
+                if (goal.matches("org\\.codehaus\\.mojo\\:exec-maven-plugin\\:(.)+\\:exec") //NOI18N
+                        || goal.indexOf("exec:exec") > -1) { //NOI18N
+                    if (map.getProperties() != null) {
+                        if (map.getProperties().containsKey("exec.args")) {
+                            String execArgs = map.getProperties().get("exec.args");
+                            if (execArgs.contains("-classpath")) {
+                                return true;
+                            }
+                        }
+                        if (map.getProperties().containsKey("exec.vmArgs")) {
+                            String execArgs = map.getProperties().get("exec.vmArgs");
+                            if (execArgs.contains("-classpath")) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        
+        public void applyToMappings() {
+            if (!(currentRun || currentDebug || currentProfile)) {
+                return;
+            }
+            
+            if (currentRun) {
+                updateAction(run, "");
+            }
+            if (currentDebug) {
+                updateAction(debug, DEFAULT_DEBUG_PARAMS);
+            }
+            if (currentProfile) {
+                updateAction(profile, "");
+            }
+        }
+        
+        private void updateAction(NetbeansActionMapping mapping, String debuVMArgs) {
+            boolean changed = false;
+            
+            if (!oldWorkDir.equals(workDir)) {
+                mapping.addProperty(RUN_WORKDIR, workDir);
+                changed = true;
+            }
+            if (!oldAppParams.equals(appParams)) {
+                mapping.addProperty(RUN_APP_PARAMS, appParams);
+                changed = true;
+            }
+            String newMainClass = this.mainClass;
+            if (newMainClass.trim().length() == 0) {
+                newMainClass = PACKAGE_CLASS_NAME_TOKEN;
+            }
+            if (!oldMainClass.equals(newMainClass)) {
+                mapping.addProperty(RUN_MAIN_CLASS, newMainClass);
+                changed = true;
+            }
+            if (!workDir.equals(oldWorkDir)) {
+                mapping.addProperty(RUN_WORKDIR, workDir);
+                changed = true;
+            }
+            String oneLineVMParams = vmParams.replace('\n', ' ');
+            String newVMParams = appendIfNotEmpty(oneLineVMParams, debuVMArgs);
+            if (!oldVmParams.equals(newVMParams)) {
+                mapping.addProperty(RUN_VM_PARAMS, newVMParams);
+                changed = true;
+            }
+            
+            if (changed) {
+                // define the properties, if not defined ...
+                Map<String, String> props = mapping.getProperties();
+                if (mapping.getProperties().get(RUN_VM_PARAMS) == null) {
+                    mapping.addProperty(RUN_VM_PARAMS, vmParams);
+                }
+                if (mapping.getProperties().get(RUN_APP_PARAMS) == null) {
+                    mapping.addProperty(RUN_APP_PARAMS, appParams);
+                }
+                if (mapping.getProperties().get(RUN_MAIN_CLASS) == null) {
+                    mapping.addProperty(RUN_MAIN_CLASS, newMainClass);
+                }
+                mapping.addProperty(RUN_PARAMS, 
+                    "${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}"
+                );
+            }
+            
+            if (changed) {
+                ModelHandle2.setUserActionMapping(mapping, goalMappings);
+                modified = true;
+            }
+        }
+    }
+    
+    /**
+     * Splits a command line, pays respect to quoting and newlines.
+     * @param line original line
+     * @return line split into individual arguments.
+     */
+    public static String[] splitCommandLine(String line) {
+        if (line == null) {
+            return new String[0];
+        }
+        String l = line.trim();
+        if (l.isEmpty()) {
+            return new String[0];
+        }
+        List<String> result = new ArrayList<>();
+        for (String part : propertySplitter(l, true)) {
+            result.add(part);
+        }
+        return result.toArray(new String[result.size()]);
+    }
+
+    private static boolean isNullOrEmpty(String s) {
+        return s == null || s.trim().isEmpty();
+    }
+    
+    /**
+     * Checks that the mapping does not specify custom exec arguments. If
+     * the exec.args is set to {@link #DEFAULT_EXEC_ARGS_CLASSPATH}, the 
+     * `packageClassName' has not been set (= unspecified).
+     * If the exec.args is set tp {@link #DEFAULT_EXEC_ARGS_CLASSPATH2}, then
+     * none of the referenced properties can provide a value (the exec.args itself
+     * is not changed by the IDE, just the referenced properties).
+     * <p>
+     * Other values of `exec.args' means user customizations.
+     * <p>
+     * Returns {@code null}, if there are user customizations. Returns the value of
+     * exec.args (the default string) so the caller can retain the parameter
+     * passing style.
+     * 
+     * @param mapp action mapping.
+     * @return 
+     */
+    public static String doesNotSpecifyCustomExecArgs(NetbeansActionMapping  mapp) {
+        return doesNotSpecifyCustomExecArgs(true, mapp.getProperties());
+    }
+    
+    private static boolean equalsOrIncludes(boolean exact, String text, String toFind) {
+        if (exact) {
+            return text.equals(toFind);
+        } else {
+            return text.contains(toFind);
+        }
+    }
+    
+    private static String maybeReplaceTemplate(String template, String mainClassReplaced) {
+        if (mainClassReplaced != null) {
+            return template.replace(PACKAGE_CLASS_NAME_TOKEN, mainClassReplaced);
+        } else {
+            return template;
+        }
+    }
+    
+    /**
+     * The inexact match is used from RunJarStartupArgs
+     */
+    public static String doesNotSpecifyCustomExecArgs(boolean exact, Map<? extends String, ? extends String> props) {
+        String execArgs = props.get(RUN_PARAMS);
+        String replacedMainClass = props.get(RUN_MAIN_CLASS);
+        String template;
+        boolean secondTry = replacedMainClass != null && execArgs.contains(PACKAGE_CLASS_NAME_TOKEN);
+        
+        template = DEFAULT_EXEC_ARGS_CLASSPATH;
+        if (equalsOrIncludes(exact, execArgs, template)) {
+            return template;
+        }
+        if (secondTry) {
+            template = template.replace(PACKAGE_CLASS_NAME_TOKEN, replacedMainClass);
+            if (equalsOrIncludes(exact, execArgs, template)) {
+                return template;
+            }
+        }
+        template = DEFAULT_EXEC_ARGS_CLASSPATH2;
+        if (!equalsOrIncludes(exact, execArgs, template)) {
+            if (!secondTry) {
+                return null;
+            }
+            template = template.replace(PACKAGE_CLASS_NAME_TOKEN, replacedMainClass);
+            if (!equalsOrIncludes(exact, execArgs, template)) {
+                return null;
+            }
+        }
+
+        if (!exact) {
+            return template;
+        }
+        
+        // none of the properties refrenced in DEFAULT_EXEC_ARGS_CLASSPATH2 is defined:
+        if (isNullOrEmpty(props.get(RUN_APP_PARAMS)) && 
+            isNullOrEmpty(props.get(RUN_VM_PARAMS))) {
+            
+            String mainClass = props.get(RUN_MAIN_CLASS);
+            if (mainClass == null ||
+                "".equals(mainClass) ||
+                MavenExecuteUtils.PACKAGE_CLASS_NAME_TOKEN.equals(mainClass)) {
+                return template;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Creates a helper to edit the mapping instances. Individual settings can be
+     * inspected by getters and changed by setters on the helper, changes can be then
+     * applied back to the mappings.
+     * @param project the target project
+     * @param run run action mapping
+     * @param debug debug action mapping
+     * @param profile profile action mapping
+     * @param goalMappings the mapping registry
+     * @return 
+     */
+    public static ExecutionEnvHelper createExecutionEnvHelper(
+            NbMavenProjectImpl project,
+            NetbeansActionMapping run,
+            NetbeansActionMapping debug,
+            NetbeansActionMapping profile,
+            ActionToGoalMapping goalMappings) {
+        return new ExecutionEnvHelper(project, run, debug, profile, goalMappings);
+    }
+    
+    /**
+     * Joins parameters into a single string. Quotes as necessary if parameters contain
+     * spaces. Checks for already quoted or escaped parameters.
+     * @param params List of parameters.
+     * @return single command line.
+     */
+    public static String joinParameters(String... params) {
+        if (params == null) {
+            return ""; // NOI18N
+        }
+        return joinParameters(Arrays.asList(params));
+    }
+    
+    private static boolean isQuoteChar(char c) {
+        return c == '\'' || c == '"';
+    }
+    
+    public static String joinParameters(List<String> params) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : params) {
+            if (s == null) {
+                continue;
+            }
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            if (s.length() > 1) {
+                char c = s.charAt(0);
+                if (isQuoteChar(c) && s.charAt(s.length() - 1) == c) {
+                    sb.append(s);
+                    continue;
+                }
+            }
+            // note: does not care about escaped spaces.
+            if (!s.contains(" ")) {
+                sb.append(s.replace("'", "\\'").replace("\"", "\\\""));
+            } else {
+                sb.append("\"").append(
+                        s.replace("\"", "\\\"")
+                ).append("\"");
+            }
+        }
+        return sb.toString();
+    }
+    
+    public static List<String> extractDebugJVMOptions(String argLine) {
+        Iterable<String> split = propertySplitter(argLine, true);
+        List<String> toRet = new ArrayList<String>();
+        for (String arg : split) {
+            if ("-Xdebug".equals(arg)) { //NOI18N
+                continue;
+            }
+            if ("-Djava.compiler=none".equals(arg)) { //NOI18N
+                continue;
+            }
+            if ("-Xnoagent".equals(arg)) { //NOI18N
+                continue;
+            }
+            if (arg.startsWith("-Xrunjdwp")) { //NOI18N
+                continue;
+            }
+            if (arg.equals("-agentlib:jdwp")) { //NOI18N
+                continue;
+            }
+            if (arg.startsWith("-agentlib:jdwp=")) { //NOI18N
+                continue;
+            }
+            if (arg.trim().length() == 0) {
+                continue;
+            }
+            toRet.add(arg);
+        }
+        return toRet;
+    }
+
+    
+    /**
+     * used by quickrun configuration.
+     * @param argline
+     * @return
+     */
+    public static String[] splitAll(String argline, boolean filterClassPath) {
+        String jvm = splitJVMParams(argline, false);
+        String mainClazz = splitMainClass(argline);
+        String args = splitParams(argline);
+        if (filterClassPath && jvm != null && jvm.contains("-classpath %classpath")) {
+            jvm = jvm.replace("-classpath %classpath", "");
+        }
+        if (mainClazz != null && mainClazz.equals("${packageClassName}")) {
+                    mainClazz = "";
+        }
+        return new String[] {
+            (jvm != null ? jvm : ""),
+            (mainClazz != null ? mainClazz : ""),
+            (args != null ? args : "")
+        };
+    }
+    
+    @NonNull
+    public static String splitJVMParams(String line, boolean newLines) {
+        PropertySplitter ps = new PropertySplitter(line);
+        ps.setSeparator(' '); //NOI18N
+        String s = ps.nextPair();
+        String jvms = ""; //NOI18N
+        while (s != null) {
+            if (s.startsWith("-") || /* #199411 */s.startsWith("\"-") || s.contains("%classpath")) { //NOI18N
+                if(s.contains("%classpath")) {
+                    jvms =  jvms + " " + s;
+                } else {
+                    jvms =  jvms + (jvms.isEmpty() ? "" : (newLines ? "\n" : " ")) + s;
+                }
+            } else if (s.equals(PACKAGE_CLASS_NAME_TOKEN) || s.equals(RUN_MAIN_CLASS_TOKEN) || s.matches("[\\w]+[\\.]{0,1}[\\w\\.]*")) { //NOI18N
+                break;
+            } else {
+                jvms =  jvms + " " + s;
+            }
+            s = ps.nextPair();
+        }
+        return jvms.trim();
+    }
+    
+    @NonNull
+    public static String splitMainClass(String line) {
+        PropertySplitter ps = new PropertySplitter(line);
+        ps.setSeparator(' '); //NOI18N
+        String s = ps.nextPair();
+        while (s != null) {
+            if (s.startsWith("-") || s.contains("%classpath")) { //NOI18N
+                s = ps.nextPair();
+                continue;
+            } else if (s.equals(PACKAGE_CLASS_NAME_TOKEN) || s.equals(RUN_MAIN_CLASS_TOKEN) || s.matches("[\\w]+[\\.]{0,1}[\\w\\.]*")) { //NOI18N
+                return s;
+            } else {
+                Logger.getLogger(RunJarPanel.class.getName()).fine("failed splitting main class from=" + line); //NOI18N
+            }
+            s = ps.nextPair();
+        }
+        return ""; //NOI18N
+    }
+    
+    @NonNull
+    public static String splitParams(String line) {
+        int argsIndex = line.indexOf(RunJarStartupArgs.USER_PROGRAM_ARGS_MARKER);
+        if (argsIndex > -1) {
+            return line.substring(argsIndex + RunJarStartupArgs.USER_PROGRAM_ARGS_MARKER.length()).trim();
+        }
+        String main = splitMainClass(line);
+        if (main.isEmpty()) {
+            return "";
+        }
+        int i = line.indexOf(main);
+        if (i > -1) {
+            return line.substring(i + main.length()).trim();
+        }
+        return ""; //NOI18N
+    }
+    /**
+     * Splits the line into sequence of arguments, respects quoting.
+     * @param line the line as a string
+     * @return arguments in an iterable
+     */
+    public static Iterable<String> propertySplitter(String line) {
+        return propertySplitter(line, true);
+    }
+    
+    public static Iterable<String> propertySplitter(String line, boolean outputQuotes) {
+        class SplitIt implements Iterator<String> {
+            private final PropertySplitter spl = new PropertySplitter(line);
+            private String nextPair;
+
+            public SplitIt() {
+                spl.setSeparator(' ');
+                spl.setOutputQuotes(outputQuotes);
+            }
+            
+            @Override
+            public boolean hasNext() {
+                if (nextPair == null) {
+                    nextPair = spl.nextPair();
+                }
+                return nextPair != null;
+            }
+
+            @Override
+            public String next() {
+                String s;
+                if (nextPair == null) {
+                    nextPair = spl.nextPair();
+                }
+                s = nextPair;
+                nextPair = null;
+                if (s != null) {
+                    return s;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        }
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                return new SplitIt();
+            }
+        };
+    }
+    
+    /**
+     *
+     * @author mkleint
+     */
+    static class PropertySplitter {

Review comment:
       Property splitter unified in 50ee5d6




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


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

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r573491819



##########
File path: java/maven/apichanges.xml
##########
@@ -83,6 +83,49 @@ is the proper place.
     <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        <change id="ProjectLookup.fallback">
+            <api name="general"/>
+            <summary>Allow project services, that are ordered <b>after</b> specific packaging type</summary>
+            <version major="2" minor="144"/>
+            <date day="10" month="2" year="2021"/>
+            <author login="sdedic"/>
+            <compatibility addition="yes" binary="compatible" semantic="compatible"/>
+            <description>
+                <p>
+                    Project lookup consists of base services, plus services for the project's packaging type. This change allows for

Review comment:
       I would expect a note describing how to register such service! Maybe a link to `architecture-summary.html#xyz`.

##########
File path: java/maven/src/org/netbeans/modules/maven/execute/defaultActionMappings.xml
##########
@@ -260,8 +274,9 @@
         <properties>
             <test>${packageClassName}</test>
             <forkMode>once</forkMode>
-            <maven.surefire.debug>${exec.args}</maven.surefire.debug>
+            <maven.surefire.debug>${exec.vmArgs}</maven.surefire.debug>
             <!-- need to declare exec.args property to engage the StartupExtender infrastructure -->
+            <exec.vmArgs/>

Review comment:
       The changes in this file are very illustrative!
   
   The comment "need to declare exec.args property" is now slightly off...

##########
File path: java/maven/arch.xml
##########
@@ -115,6 +115,13 @@
        will be merged into a single instance in the project's lookup.
      </p>
     </api>
+    <api group="layer" name="MavenPackagingLookup" type="export" category="devel">
+        <p>
+            <code>Projects/org-netbeans-modules-maven/&lt;packaging-type>/Lookup</code> is added to the project's additional Lookup. The content is expected
+            to contain packaing-specific services and processors, for example, <a href="@TOP@/org/netbans/modules/maven/api/execute/PrerequisitesChecker.html">PrerequisitesCheckers</a>.
+            In addition, <code>Projects/org-netbeans-modules-maven/_last/Lookup</code> defines services that act after the packaging-specific ones.

Review comment:
       Naming is hard: `_last`!? Any? Generic? Fallback? Why there is the `_`? 

##########
File path: java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
##########
@@ -100,10 +103,28 @@
 
 /**
  * support for executing maven, externally on the command line.
+ * <b>Since 2/1.144</b>, the {@link LateBoundPrerequisitesChecker} registered in Maven projects for JAR packaging 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>launcherArgs</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:
+ * {@codesnippet MavenExecutionTestBase#samplePassAdditionalVMargs}
+ * The example will <b>append</b> <code>-DvmArg2=2</code> to VM arguments and <b>replaces</b> all user
+ * program arguments with <code>"paramY"</code>. Append mode can be controlled using {@link ExplicitProcessParameters.Builder#appendArgs} or
+ * {@link ExplicitProcessParameters.Builder#appendPriorityArgs}.
+ * 
  * @author  Milos Kleint (mkleint@codehaus.org)
+ * @author  Svata Dedic (svatopluk.dedic@gmail.com)
  */
 public class MavenCommandLineExecutor extends AbstractMavenExecutor {
     static final String ENV_PREFIX = "Env."; //NOI18N
+    static final String INTERNAL_PREFIX = "NbIde."; //NOI18N

Review comment:
       Is this introducing some form of property based API?

##########
File path: java/maven/test/unit/src/org/netbeans/modules/maven/runjar/MavenExecuteUtilsTest.java
##########
@@ -0,0 +1,191 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author sdedic
+ */
+public class MavenExecuteUtilsTest {

Review comment:
       Can you make sure these tests are executed in a Travis gate?

##########
File path: java/maven/arch.xml
##########
@@ -186,6 +193,20 @@
           to specify the default behavior of compile on save in Maven 
           projects.
       </api> 
+      <api category="devel" group="property" name="exec.vmArgs" type="export">
+          The plugin exports Java VM parameters to be used for application execution in <code>${exec.vmArgs}</code> 
+          property that can be used in action mappings or Maven pom.xml.
+      </api>
+      <api category="devel" group="property" name="exec.appArgs" type="export">
+          The plugin exports application parameters to be used for application execution in <code>${exec.appArgs}</code> 
+          property that can be used in action mappings or Maven pom.xml.
+      </api>
+      <api category="devel" group="lookup" name="ExplicitProcessParameters" type="export">

Review comment:
       In this case `devel` is fine (even I would prefer higher commitment ;-).

##########
File path: java/maven/arch.xml
##########
@@ -186,6 +193,20 @@
           to specify the default behavior of compile on save in Maven 
           projects.
       </api> 
+      <api category="devel" group="property" name="exec.vmArgs" type="export">

Review comment:
       As this is going to be used in end-user projects configurations, I am afraid `devel` is a bit too low category in the long term.

##########
File path: java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
##########
@@ -0,0 +1,859 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.maven.NbMavenProjectImpl;
+import org.netbeans.modules.maven.api.customizer.ModelHandle2;
+import org.netbeans.modules.maven.customizer.RunJarPanel;
+import org.netbeans.modules.maven.execute.ActionToGoalUtils;
+import org.netbeans.modules.maven.execute.model.ActionToGoalMapping;
+import org.netbeans.modules.maven.execute.model.NetbeansActionMapping;
+import org.netbeans.modules.maven.runjar.RunJarStartupArgs;
+import org.netbeans.spi.project.ActionProvider;
+
+/**
+ *
+ * @author sdedic
+ */
+public final class MavenExecuteUtils {
+    /**
+     * Hint for the default PrereqqCheckeers that explicit parameters have been already processed.
+     * Will not be propagated to maven process.
+     */
+    public static final String RUN_EXPLICIT_PROCESSED = "NbIde.ExplicitParametersApplied"; // NOI18N

Review comment:
       Here is the use of the `NbIde.` internal property contract. To be "pintlich" it should be mentioned in `arch.xml` as `private` (as the declaration and usage stay inside of a single module) API.




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579039611



##########
File path: java/maven/arch.xml
##########
@@ -115,6 +115,13 @@
        will be merged into a single instance in the project's lookup.
      </p>
     </api>
+    <api group="layer" name="MavenPackagingLookup" type="export" category="devel">
+        <p>
+            <code>Projects/org-netbeans-modules-maven/&lt;packaging-type>/Lookup</code> is added to the project's additional Lookup. The content is expected
+            to contain packaing-specific services and processors, for example, <a href="@TOP@/org/netbans/modules/maven/api/execute/PrerequisitesChecker.html">PrerequisitesCheckers</a>.
+            In addition, <code>Projects/org-netbeans-modules-maven/_last/Lookup</code> defines services that act after the packaging-specific ones.

Review comment:
       No further feedback, so I've changed to `_any` (154603c504) and documented the ordering in 9d7654ce75




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178041



##########
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:
       Documented in 39417f0d8b 




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572717040



##########
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:
       Addressed in 600178d5af

##########
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:
       Addressed in 600178d5af

##########
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:
       Done in 600178d5af




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-783975242


   No response from other reviewers than @JaroslavTulach for 5 days (positive or negative), except positive from @JaroslavTulach. Tests "passing" (failure is a timeout, not a layer error). Merging.
   
   Thanks for your reviews and feedback.


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178589



##########
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:
       Documented in 39417f0d8b




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-781140970


   @entlicher I've joined the 2 impls of `PropertySplitt` in the `runjar` package (`customizer` pkg already depended on `runjar`, not the other way around). 
   During that, I've noticed dead code in `RunJarPanel` (and a little in `MavenExecuteUtils`) and deleted it - results in moving the only tests for `RunJarPanel` which actually tested parameter splitting now done by `MavenExecuteUtils`.
   
   Please re-review commit 50ee5d6


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178346



##########
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:
       Documented in 39417f0d8b 




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572158785



##########
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:
       Addressed in a4f7f08c1d




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572177832



##########
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:
       Reworded in ef8b981300




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572159675



##########
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:
       Removed in 39417f0d8b




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579039803



##########
File path: java/maven/apichanges.xml
##########
@@ -83,6 +83,49 @@ is the proper place.
     <!-- ACTUAL CHANGES BEGIN HERE: -->
 
     <changes>
+        <change id="ProjectLookup.fallback">
+            <api name="general"/>
+            <summary>Allow project services, that are ordered <b>after</b> specific packaging type</summary>
+            <version major="2" minor="144"/>
+            <date day="10" month="2" year="2021"/>
+            <author login="sdedic"/>
+            <compatibility addition="yes" binary="compatible" semantic="compatible"/>
+            <description>
+                <p>
+                    Project lookup consists of base services, plus services for the project's packaging type. This change allows for

Review comment:
       Documented in 9d7654ce75




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


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

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r573490518



##########
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:
       Category "devel": Under development is a name for a contract that is expected to become a stable API, but that has not yet been finished. The current state serves as a proof of concept, and others are encourage to try it and comment on a dedicated mailing list. Incompatible changes may be done between releases, but should be rare, not radical and properly announced on the mailing list.
   
   Fine with me. Over time this should however become category "stable".
   
   Stable interfaces are those that have received a final state and the maintainers are ready to support it forever and never change them incompatibly. The "forever" and "never" should not be taken as absolute: It is possible to change the contract, but only in major versions and only after a careful considerations and in cases where it is imperative that a change be made. Stable contracts should [#poi preserve the investments ] of those entering into them (users of an API). 




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r580072857



##########
File path: java/maven/src/org/netbeans/modules/maven/execute/defaultActionMappings.xml
##########
@@ -260,8 +274,9 @@
         <properties>
             <test>${packageClassName}</test>
             <forkMode>once</forkMode>
-            <maven.surefire.debug>${exec.args}</maven.surefire.debug>
+            <maven.surefire.debug>${exec.vmArgs}</maven.surefire.debug>
             <!-- need to declare exec.args property to engage the StartupExtender infrastructure -->
+            <exec.vmArgs/>

Review comment:
       Fixed in af77814




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572716710



##########
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:
       Addressed in 600178d5af




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579038989



##########
File path: java/maven/arch.xml
##########
@@ -186,6 +193,20 @@
           to specify the default behavior of compile on save in Maven 
           projects.
       </api> 
+      <api category="devel" group="property" name="exec.vmArgs" type="export">

Review comment:
       Changed to `stable` in 9d7654ce75




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572170637



##########
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:
       Uh-oh !
   Fixed in 247e807afc, relevant test added.




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572160283



##########
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:
       Documented in 39417f0d8b 




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


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

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r573490518



##########
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:
       Category "devel": Under development is a name for a contract that is expected to become a stable API, but that has not yet been finished. The current state serves as a proof of concept, and others are encourage to try it and comment on a dedicated mailing list. Incompatible changes may be done between releases, but should be rare, not radical and properly announced on the mailing list.
   
   Fine with me. Over time this should however become category "stable".
   
   [Stable interfaces](Stable) are those that have received a final state and the maintainers are ready to support it forever and never change them incompatibly. The "forever" and "never" should not be taken as absolute: It is possible to change the contract, but only in major versions and only after a careful considerations and in cases where it is imperative that a change be made. Stable contracts should [#poi preserve the investments ] of those entering into them (users of an API). 




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


[GitHub] [netbeans] sdedic edited a comment on pull request #2731: Support for VM/app argument injection to (maven) run and debug project actions

Posted by GitBox <gi...@apache.org>.
sdedic edited a comment on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-783463378


   I've adressed the remaining review comments. Now I've restructured + squashed the commits so review comment changes are merged to their appropriate 'features'. @JaroslavTulach  @entlicher  - please do a final approval, so I can finish this work.
   


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572160472



##########
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:
       Codesnippet samples added in 39417f0d8b




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r573524177



##########
File path: java/maven/arch.xml
##########
@@ -115,6 +115,13 @@
        will be merged into a single instance in the project's lookup.
      </p>
     </api>
+    <api group="layer" name="MavenPackagingLookup" type="export" category="devel">
+        <p>
+            <code>Projects/org-netbeans-modules-maven/&lt;packaging-type>/Lookup</code> is added to the project's additional Lookup. The content is expected
+            to contain packaing-specific services and processors, for example, <a href="@TOP@/org/netbans/modules/maven/api/execute/PrerequisitesChecker.html">PrerequisitesCheckers</a>.
+            In addition, <code>Projects/org-netbeans-modules-maven/_last/Lookup</code> defines services that act after the packaging-specific ones.

Review comment:
       Yes, it's hard. Since the name shares 'namespace' with packaging type setting in maven `pom.xml`, I wanted to include some awkward character not to clash with possible future additional packaging type. **_any** would be more descriptive, thanks.




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579038594



##########
File path: java/maven/src/org/netbeans/modules/maven/execute/MavenCommandLineExecutor.java
##########
@@ -100,10 +103,28 @@
 
 /**
  * support for executing maven, externally on the command line.
+ * <b>Since 2/1.144</b>, the {@link LateBoundPrerequisitesChecker} registered in Maven projects for JAR packaging 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>launcherArgs</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:
+ * {@codesnippet MavenExecutionTestBase#samplePassAdditionalVMargs}
+ * The example will <b>append</b> <code>-DvmArg2=2</code> to VM arguments and <b>replaces</b> all user
+ * program arguments with <code>"paramY"</code>. Append mode can be controlled using {@link ExplicitProcessParameters.Builder#appendArgs} or
+ * {@link ExplicitProcessParameters.Builder#appendPriorityArgs}.
+ * 
  * @author  Milos Kleint (mkleint@codehaus.org)
+ * @author  Svata Dedic (svatopluk.dedic@gmail.com)
  */
 public class MavenCommandLineExecutor extends AbstractMavenExecutor {
     static final String ENV_PREFIX = "Env."; //NOI18N
+    static final String INTERNAL_PREFIX = "NbIde."; //NOI18N

Review comment:
       Documented in 9d7654ce75




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r579038471



##########
File path: java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
##########
@@ -0,0 +1,859 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.maven.NbMavenProjectImpl;
+import org.netbeans.modules.maven.api.customizer.ModelHandle2;
+import org.netbeans.modules.maven.customizer.RunJarPanel;
+import org.netbeans.modules.maven.execute.ActionToGoalUtils;
+import org.netbeans.modules.maven.execute.model.ActionToGoalMapping;
+import org.netbeans.modules.maven.execute.model.NetbeansActionMapping;
+import org.netbeans.modules.maven.runjar.RunJarStartupArgs;
+import org.netbeans.spi.project.ActionProvider;
+
+/**
+ *
+ * @author sdedic
+ */
+public final class MavenExecuteUtils {
+    /**
+     * Hint for the default PrereqqCheckeers that explicit parameters have been already processed.
+     * Will not be propagated to maven process.
+     */
+    public static final String RUN_EXPLICIT_PROCESSED = "NbIde.ExplicitParametersApplied"; // NOI18N

Review comment:
       Documented in 9d7654ce75




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572026830



##########
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:
       Will add javadoc. Unlike the other helpers, where the client's reaction to "unspecified" may vary, I'd like to retain this helper method: it's convenient to use the vararg form `getAllArguments("${pkgMainClass}")`, and in other situations where the 'middle' part is already parsed out, the `List<String>` form is more convenient: `getAllArguments(cmdLineParts)`. Code that converts List<String> -> String[] is very pleasant, isn't it ?




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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572178589



##########
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:
       Documented in 39417f0d8b 




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


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

Posted by GitBox <gi...@apache.org>.
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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-775105741


   I'll recommit the PR with Jarda's fix & corrections. But will **remove** changes to Gradle, as they are not complete - will include them in another PR.


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572180347



##########
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:
       OK, the terminology sins start to pile up. So what about to:
   - rename `priorityArgs` -> `launcherArgs`. VM arguments, launcher script arguments, ...
   - rename `append(Launcher)Args` -> `replace(Launcher)Args` + invert the logic (true -> replace)
   - rename `rank` to `position` (better than priority, if sorted lowest-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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r571875361



##########
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:
       I must have screwed up something during the "last" rounds of test fixes. Will fix - thanks for catching.




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


[GitHub] [netbeans] sdedic merged pull request #2731: Support for VM/app argument injection to (maven) run and debug project actions

Posted by GitBox <gi...@apache.org>.
sdedic merged pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731


   


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-783463378


   I've adressed the remaining review comments. Now I've restructured + squashed the commits so review comment changes are merged to their appropriate 'features'. @JaroslavTulach  @mentlicher - please do a final approval, so I can finish this work.
   


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


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

Posted by GitBox <gi...@apache.org>.
sdedic commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r572160283



##########
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:
       Documented in 39417f0d8b

##########
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:
       Given as non-normative example in 39417f0d8b




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


[GitHub] [netbeans] sdedic edited a comment on pull request #2731: Support for VM/app argument injection to (maven) run and debug project actions

Posted by GitBox <gi...@apache.org>.
sdedic edited a comment on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-781140970


   @entlicher I've joined the 2 impls of `PropertySplitter` in the `runjar` package (`customizer` pkg already depended on `runjar`, not the other way around). 
   During that, I've noticed dead code in `RunJarPanel` (and a little in `MavenExecuteUtils`) and deleted it - results in moving the only tests for `RunJarPanel` which actually tested parameter splitting now done by `MavenExecuteUtils`.
   
   Please re-review commit 50ee5d6


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


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

Posted by GitBox <gi...@apache.org>.
entlicher commented on a change in pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#discussion_r577661130



##########
File path: java/maven/src/org/netbeans/modules/maven/runjar/MavenExecuteUtils.java
##########
@@ -0,0 +1,859 @@
+/*
+ * 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.modules.maven.runjar;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.maven.NbMavenProjectImpl;
+import org.netbeans.modules.maven.api.customizer.ModelHandle2;
+import org.netbeans.modules.maven.customizer.RunJarPanel;
+import org.netbeans.modules.maven.execute.ActionToGoalUtils;
+import org.netbeans.modules.maven.execute.model.ActionToGoalMapping;
+import org.netbeans.modules.maven.execute.model.NetbeansActionMapping;
+import org.netbeans.modules.maven.runjar.RunJarStartupArgs;
+import org.netbeans.spi.project.ActionProvider;
+
+/**
+ *
+ * @author sdedic
+ */
+public final class MavenExecuteUtils {
+    /**
+     * Hint for the default PrereqqCheckeers that explicit parameters have been already processed.
+     * Will not be propagated to maven process.
+     */
+    public static final String RUN_EXPLICIT_PROCESSED = "NbIde.ExplicitParametersApplied"; // NOI18N
+    
+    /**
+     * Name of the property for VM arguments.
+     * @since 2.144
+     */
+    public static final String RUN_VM_PARAMS = "exec.vmArgs"; //NOI18N
+
+    /**
+     * Name of the property to pass main class. ${packageClassName} works as well.
+     * @since 2.144
+     */
+    public static final String RUN_MAIN_CLASS = "exec.mainClass"; //NOI18N
+
+    /**
+     * Name of the property for applicaiton arguments.
+     * @since 2.144
+     */
+    public static final String RUN_APP_PARAMS = "exec.appArgs"; //NOI18N
+
+    /**
+     * Name of the property that collects the entire command line.
+     */
+    public static final String RUN_PARAMS = "exec.args"; //NOI18N
+    
+    /**
+     * Name of the property for working directory passed to th exec plugin
+     */
+    public static final String RUN_WORKDIR = "exec.workingdir"; //NOI18N
+    
+    private static final String RUN_VM_PARAMS_TOKEN = "${" + RUN_VM_PARAMS + "}"; //NOI18N
+    private static final String RUN_APP_PARAMS_TOKEN = "${" + RUN_APP_PARAMS + "}"; //NOI18N
+    private static final String RUN_MAIN_CLASS_TOKEN = "${" + RUN_MAIN_CLASS + "}"; //NOI18N
+    static final String PACKAGE_CLASS_NAME_TOKEN = "${packageClassName}"; //NOI18N
+    
+    public static final String EXEC_ARGS_CLASSPATH_TOKEN = "-classpath %classpath"; // NOI18N
+    public static final String DEFAULT_EXEC_ARGS_CLASSPATH = EXEC_ARGS_CLASSPATH_TOKEN + " ${packageClassName}"; // NOI18N
+    static final String DEFAULT_DEBUG_PARAMS = "-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}"; //NOI18N
+    static final String DEFAULT_EXEC_ARGS_CLASSPATH2 =  "${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}"; // NOI18N
+
+    /**
+     * ID of the 'profile' action.
+     */
+    public static final String PROFILE_CMD = "profile"; // NOI18N
+    
+    /**
+     * A helper that can update action mappings based on changes
+     * made on the helper instance. Use {@link #createExecutionEnvHelper}
+     * to make an instance.
+     */
+    public final static class ExecutionEnvHelper {
+        private final ActionToGoalMapping goalMappings;
+        private final NbMavenProjectImpl project;
+        
+        private String oldAllParams;
+        private String oldVmParams;
+        private String oldAppParams;
+        private String oldWorkDir;
+        private String oldMainClass;
+        
+        private boolean currentRun;
+        private boolean currentDebug;
+        private boolean currentProfile;
+        
+        private String execParams;
+        private String vmParams;
+        private String appParams;
+        private String workDir;
+        private String mainClass;
+        
+        private NetbeansActionMapping run;
+        private NetbeansActionMapping debug;
+        private NetbeansActionMapping profile;
+
+        private boolean mergedConfig;
+        private boolean modified;
+
+        ExecutionEnvHelper(
+                NbMavenProjectImpl project,
+                NetbeansActionMapping run,
+                NetbeansActionMapping debug,
+                NetbeansActionMapping profile,
+                ActionToGoalMapping goalMappings) {
+            this.project = project;
+            this.goalMappings = goalMappings;
+            this.run = run;
+            this.debug = debug;
+            this.profile = profile;
+        }
+        
+        private String fallbackParams(String paramName, boolean stripDebug) {
+            String val = run != null ? run.getProperties().get(paramName) : null;
+            if (val == null && debug != null) {
+                val = debug.getProperties().get(paramName);
+                if (val != null && stripDebug) {
+                    val = String.join(" ", extractDebugJVMOptions(val));
+                }
+            }
+            if (val == null && profile != null) {
+                val = profile.getProperties().get(paramName);
+            }
+            return val == null ? "" : val.trim(); // NOI18N
+        }
+
+        private String appendIfNotEmpty(String a, String b) {
+            if (a == null || a.isEmpty()) {
+                return b;
+            }
+            if (b == null || b.isEmpty()) {
+                return a;
+            }
+            return a + " " + b;
+        }
+
+        public ActionToGoalMapping getGoalMappings() {
+            return goalMappings;
+        }
+
+        public boolean isModified() {
+            return modified;
+        }
+
+        public boolean isValid() {
+            return currentRun && currentDebug && currentProfile;
+        }
+
+        public boolean isCurrentRun() {
+            return currentRun;
+        }
+
+        public boolean isCurrentDebug() {
+            return currentDebug;
+        }
+
+        public boolean isCurrentProfile() {
+            return currentProfile;
+        }
+        
+        public void setMainClass(String mainClass) {
+            this.mainClass = mainClass;
+        }
+
+        public void setExecParams(String execParams) {
+            this.execParams = execParams;
+        }
+
+        public void setVmParams(String vmParams) {
+            this.vmParams = vmParams;
+        }
+
+        public void setAppParams(String appParams) {
+            this.appParams = appParams;
+        }
+
+        public NbMavenProjectImpl getProject() {
+            return project;
+        }
+
+        public String getWorkDir() {
+            return oldWorkDir;
+        }
+
+        public void setWorkDir(String workDir) {
+            this.workDir = workDir;
+        }
+
+        public String getMainClass() {
+            return mainClass;
+        }
+
+        public NetbeansActionMapping getRun() {
+            return run;
+        }
+
+        public NetbeansActionMapping getProfile() {
+            return profile;
+        }
+
+        public String getAllParams() {
+            return oldAllParams;
+        }
+
+        public String getVmParams() {
+            return vmParams;
+        }
+
+        public String getAppParams() {
+            return appParams;
+        }
+        
+        private NetbeansActionMapping getMapping(String a) {
+            NetbeansActionMapping m = ActionToGoalUtils.getDefaultMapping(a, project);
+            return m;
+        }
+        
+        /**
+         * Loads and parses values from the project's nbactions.xml
+         */
+        public void loadFromProject() {
+            NetbeansActionMapping m;
+            
+            if (run == null) {
+                run = getMapping(ActionProvider.COMMAND_RUN);
+            }
+            if (debug == null) {
+                debug = getMapping(ActionProvider.COMMAND_DEBUG);
+            }
+            if (profile == null) {
+                profile = getMapping(PROFILE_CMD);
+            }
+            
+            currentRun = checkNewMapping(run);
+            currentDebug = checkNewMapping(debug);
+            currentProfile = checkNewMapping(profile);
+            
+            oldWorkDir = fallbackParams(RUN_WORKDIR, false);
+            oldAllParams = fallbackParams(RUN_PARAMS, false);
+            oldVmParams = fallbackParams(RUN_VM_PARAMS, true);
+            oldAppParams = fallbackParams(RUN_APP_PARAMS, false);
+            oldMainClass = fallbackParams(RUN_MAIN_CLASS, false);
+            
+            mergedConfig = (oldVmParams.isEmpty() && oldAppParams.isEmpty() && oldMainClass.isEmpty());
+            
+            appendVMParamsFromOldParams();
+            addAppParamsFromOldParams();
+            loadMainClass();
+            
+            workDir = oldWorkDir;
+            vmParams = oldVmParams;
+            appParams = oldAppParams;
+            mainClass = oldMainClass;
+        }
+        
+        private String eraseTokens(String original, boolean withNewlines, String... tokens) {
+            StringBuilder sb = new StringBuilder();
+            for (String p : tokens) {
+                if (sb.length() > 0) {
+                    sb.append("|");
+                }
+                sb.append(Pattern.quote(p));
+                if (withNewlines) {
+                    sb.append("\\n?");
+                }
+            }
+            return original.replaceAll(sb.toString(), "").trim();
+        }
+        
+        private void appendVMParamsFromOldParams() {
+            String oldSplitVMParams = splitJVMParams(oldAllParams, true);
+            if (!oldSplitVMParams.isEmpty()) {
+                // try to get VM arguments out of all exec.args, but ignore -classpath added automatically, and
+                // exec.vmArgs present / added by default.
+                oldSplitVMParams = eraseTokens(oldSplitVMParams, true, "-classpath %classpath", RUN_VM_PARAMS_TOKEN);
+                oldVmParams = appendIfNotEmpty(oldVmParams, oldSplitVMParams);
+            }
+        }
+        
+        private void addAppParamsFromOldParams() {
+            String p = splitParams(oldAllParams);
+            if (!p.isEmpty()) {
+                p = eraseTokens(p, false, RUN_APP_PARAMS_TOKEN);
+                oldAppParams = appendIfNotEmpty(oldAppParams, p);
+            }
+        }
+        
+        private void loadMainClass() {
+            if (oldMainClass.trim().isEmpty()) {
+                oldMainClass = splitMainClass(oldAllParams);
+                // splitMainClass is never null
+            }
+            if (PACKAGE_CLASS_NAME_TOKEN.equals(oldMainClass) || RUN_MAIN_CLASS_TOKEN.equals(oldMainClass)) {
+                oldMainClass = "";
+            }
+        }
+
+        private boolean checkNewMapping(NetbeansActionMapping map) {
+            if (map == null || map.getGoals() == null) {
+                return false; //#164323
+            }
+            Iterator it = map.getGoals().iterator();
+            while (it.hasNext()) {
+                String goal = (String) it.next();
+                if (goal.matches("org\\.codehaus\\.mojo\\:exec-maven-plugin\\:(.)+\\:exec") //NOI18N
+                        || goal.indexOf("exec:exec") > -1) { //NOI18N
+                    if (map.getProperties() != null) {
+                        if (map.getProperties().containsKey("exec.args")) {
+                            String execArgs = map.getProperties().get("exec.args");
+                            if (execArgs.contains("-classpath")) {
+                                return true;
+                            }
+                        }
+                        if (map.getProperties().containsKey("exec.vmArgs")) {
+                            String execArgs = map.getProperties().get("exec.vmArgs");
+                            if (execArgs.contains("-classpath")) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        
+        public void applyToMappings() {
+            if (!(currentRun || currentDebug || currentProfile)) {
+                return;
+            }
+            
+            if (currentRun) {
+                updateAction(run, "");
+            }
+            if (currentDebug) {
+                updateAction(debug, DEFAULT_DEBUG_PARAMS);
+            }
+            if (currentProfile) {
+                updateAction(profile, "");
+            }
+        }
+        
+        private void updateAction(NetbeansActionMapping mapping, String debuVMArgs) {
+            boolean changed = false;
+            
+            if (!oldWorkDir.equals(workDir)) {
+                mapping.addProperty(RUN_WORKDIR, workDir);
+                changed = true;
+            }
+            if (!oldAppParams.equals(appParams)) {
+                mapping.addProperty(RUN_APP_PARAMS, appParams);
+                changed = true;
+            }
+            String newMainClass = this.mainClass;
+            if (newMainClass.trim().length() == 0) {
+                newMainClass = PACKAGE_CLASS_NAME_TOKEN;
+            }
+            if (!oldMainClass.equals(newMainClass)) {
+                mapping.addProperty(RUN_MAIN_CLASS, newMainClass);
+                changed = true;
+            }
+            if (!workDir.equals(oldWorkDir)) {
+                mapping.addProperty(RUN_WORKDIR, workDir);
+                changed = true;
+            }
+            String oneLineVMParams = vmParams.replace('\n', ' ');
+            String newVMParams = appendIfNotEmpty(oneLineVMParams, debuVMArgs);
+            if (!oldVmParams.equals(newVMParams)) {
+                mapping.addProperty(RUN_VM_PARAMS, newVMParams);
+                changed = true;
+            }
+            
+            if (changed) {
+                // define the properties, if not defined ...
+                Map<String, String> props = mapping.getProperties();
+                if (mapping.getProperties().get(RUN_VM_PARAMS) == null) {
+                    mapping.addProperty(RUN_VM_PARAMS, vmParams);
+                }
+                if (mapping.getProperties().get(RUN_APP_PARAMS) == null) {
+                    mapping.addProperty(RUN_APP_PARAMS, appParams);
+                }
+                if (mapping.getProperties().get(RUN_MAIN_CLASS) == null) {
+                    mapping.addProperty(RUN_MAIN_CLASS, newMainClass);
+                }
+                mapping.addProperty(RUN_PARAMS, 
+                    "${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}"
+                );
+            }
+            
+            if (changed) {
+                ModelHandle2.setUserActionMapping(mapping, goalMappings);
+                modified = true;
+            }
+        }
+    }
+    
+    /**
+     * Splits a command line, pays respect to quoting and newlines.
+     * @param line original line
+     * @return line split into individual arguments.
+     */
+    public static String[] splitCommandLine(String line) {
+        if (line == null) {
+            return new String[0];
+        }
+        String l = line.trim();
+        if (l.isEmpty()) {
+            return new String[0];
+        }
+        List<String> result = new ArrayList<>();
+        for (String part : propertySplitter(l, true)) {
+            result.add(part);
+        }
+        return result.toArray(new String[result.size()]);
+    }
+
+    private static boolean isNullOrEmpty(String s) {
+        return s == null || s.trim().isEmpty();
+    }
+    
+    /**
+     * Checks that the mapping does not specify custom exec arguments. If
+     * the exec.args is set to {@link #DEFAULT_EXEC_ARGS_CLASSPATH}, the 
+     * `packageClassName' has not been set (= unspecified).
+     * If the exec.args is set tp {@link #DEFAULT_EXEC_ARGS_CLASSPATH2}, then
+     * none of the referenced properties can provide a value (the exec.args itself
+     * is not changed by the IDE, just the referenced properties).
+     * <p>
+     * Other values of `exec.args' means user customizations.
+     * <p>
+     * Returns {@code null}, if there are user customizations. Returns the value of
+     * exec.args (the default string) so the caller can retain the parameter
+     * passing style.
+     * 
+     * @param mapp action mapping.
+     * @return 
+     */
+    public static String doesNotSpecifyCustomExecArgs(NetbeansActionMapping  mapp) {
+        return doesNotSpecifyCustomExecArgs(true, mapp.getProperties());
+    }
+    
+    private static boolean equalsOrIncludes(boolean exact, String text, String toFind) {
+        if (exact) {
+            return text.equals(toFind);
+        } else {
+            return text.contains(toFind);
+        }
+    }
+    
+    private static String maybeReplaceTemplate(String template, String mainClassReplaced) {
+        if (mainClassReplaced != null) {
+            return template.replace(PACKAGE_CLASS_NAME_TOKEN, mainClassReplaced);
+        } else {
+            return template;
+        }
+    }
+    
+    /**
+     * The inexact match is used from RunJarStartupArgs
+     */
+    public static String doesNotSpecifyCustomExecArgs(boolean exact, Map<? extends String, ? extends String> props) {
+        String execArgs = props.get(RUN_PARAMS);
+        String replacedMainClass = props.get(RUN_MAIN_CLASS);
+        String template;
+        boolean secondTry = replacedMainClass != null && execArgs.contains(PACKAGE_CLASS_NAME_TOKEN);
+        
+        template = DEFAULT_EXEC_ARGS_CLASSPATH;
+        if (equalsOrIncludes(exact, execArgs, template)) {
+            return template;
+        }
+        if (secondTry) {
+            template = template.replace(PACKAGE_CLASS_NAME_TOKEN, replacedMainClass);
+            if (equalsOrIncludes(exact, execArgs, template)) {
+                return template;
+            }
+        }
+        template = DEFAULT_EXEC_ARGS_CLASSPATH2;
+        if (!equalsOrIncludes(exact, execArgs, template)) {
+            if (!secondTry) {
+                return null;
+            }
+            template = template.replace(PACKAGE_CLASS_NAME_TOKEN, replacedMainClass);
+            if (!equalsOrIncludes(exact, execArgs, template)) {
+                return null;
+            }
+        }
+
+        if (!exact) {
+            return template;
+        }
+        
+        // none of the properties refrenced in DEFAULT_EXEC_ARGS_CLASSPATH2 is defined:
+        if (isNullOrEmpty(props.get(RUN_APP_PARAMS)) && 
+            isNullOrEmpty(props.get(RUN_VM_PARAMS))) {
+            
+            String mainClass = props.get(RUN_MAIN_CLASS);
+            if (mainClass == null ||
+                "".equals(mainClass) ||
+                MavenExecuteUtils.PACKAGE_CLASS_NAME_TOKEN.equals(mainClass)) {
+                return template;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Creates a helper to edit the mapping instances. Individual settings can be
+     * inspected by getters and changed by setters on the helper, changes can be then
+     * applied back to the mappings.
+     * @param project the target project
+     * @param run run action mapping
+     * @param debug debug action mapping
+     * @param profile profile action mapping
+     * @param goalMappings the mapping registry
+     * @return 
+     */
+    public static ExecutionEnvHelper createExecutionEnvHelper(
+            NbMavenProjectImpl project,
+            NetbeansActionMapping run,
+            NetbeansActionMapping debug,
+            NetbeansActionMapping profile,
+            ActionToGoalMapping goalMappings) {
+        return new ExecutionEnvHelper(project, run, debug, profile, goalMappings);
+    }
+    
+    /**
+     * Joins parameters into a single string. Quotes as necessary if parameters contain
+     * spaces. Checks for already quoted or escaped parameters.
+     * @param params List of parameters.
+     * @return single command line.
+     */
+    public static String joinParameters(String... params) {
+        if (params == null) {
+            return ""; // NOI18N
+        }
+        return joinParameters(Arrays.asList(params));
+    }
+    
+    private static boolean isQuoteChar(char c) {
+        return c == '\'' || c == '"';
+    }
+    
+    public static String joinParameters(List<String> params) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : params) {
+            if (s == null) {
+                continue;
+            }
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            if (s.length() > 1) {
+                char c = s.charAt(0);
+                if (isQuoteChar(c) && s.charAt(s.length() - 1) == c) {
+                    sb.append(s);
+                    continue;
+                }
+            }
+            // note: does not care about escaped spaces.
+            if (!s.contains(" ")) {
+                sb.append(s.replace("'", "\\'").replace("\"", "\\\""));
+            } else {
+                sb.append("\"").append(
+                        s.replace("\"", "\\\"")
+                ).append("\"");
+            }
+        }
+        return sb.toString();
+    }
+    
+    public static List<String> extractDebugJVMOptions(String argLine) {
+        Iterable<String> split = propertySplitter(argLine, true);
+        List<String> toRet = new ArrayList<String>();
+        for (String arg : split) {
+            if ("-Xdebug".equals(arg)) { //NOI18N
+                continue;
+            }
+            if ("-Djava.compiler=none".equals(arg)) { //NOI18N
+                continue;
+            }
+            if ("-Xnoagent".equals(arg)) { //NOI18N
+                continue;
+            }
+            if (arg.startsWith("-Xrunjdwp")) { //NOI18N
+                continue;
+            }
+            if (arg.equals("-agentlib:jdwp")) { //NOI18N
+                continue;
+            }
+            if (arg.startsWith("-agentlib:jdwp=")) { //NOI18N
+                continue;
+            }
+            if (arg.trim().length() == 0) {
+                continue;
+            }
+            toRet.add(arg);
+        }
+        return toRet;
+    }
+
+    
+    /**
+     * used by quickrun configuration.
+     * @param argline
+     * @return
+     */
+    public static String[] splitAll(String argline, boolean filterClassPath) {
+        String jvm = splitJVMParams(argline, false);
+        String mainClazz = splitMainClass(argline);
+        String args = splitParams(argline);
+        if (filterClassPath && jvm != null && jvm.contains("-classpath %classpath")) {
+            jvm = jvm.replace("-classpath %classpath", "");
+        }
+        if (mainClazz != null && mainClazz.equals("${packageClassName}")) {
+                    mainClazz = "";
+        }
+        return new String[] {
+            (jvm != null ? jvm : ""),
+            (mainClazz != null ? mainClazz : ""),
+            (args != null ? args : "")
+        };
+    }
+    
+    @NonNull
+    public static String splitJVMParams(String line, boolean newLines) {
+        PropertySplitter ps = new PropertySplitter(line);
+        ps.setSeparator(' '); //NOI18N
+        String s = ps.nextPair();
+        String jvms = ""; //NOI18N
+        while (s != null) {
+            if (s.startsWith("-") || /* #199411 */s.startsWith("\"-") || s.contains("%classpath")) { //NOI18N
+                if(s.contains("%classpath")) {
+                    jvms =  jvms + " " + s;
+                } else {
+                    jvms =  jvms + (jvms.isEmpty() ? "" : (newLines ? "\n" : " ")) + s;
+                }
+            } else if (s.equals(PACKAGE_CLASS_NAME_TOKEN) || s.equals(RUN_MAIN_CLASS_TOKEN) || s.matches("[\\w]+[\\.]{0,1}[\\w\\.]*")) { //NOI18N
+                break;
+            } else {
+                jvms =  jvms + " " + s;
+            }
+            s = ps.nextPair();
+        }
+        return jvms.trim();
+    }
+    
+    @NonNull
+    public static String splitMainClass(String line) {
+        PropertySplitter ps = new PropertySplitter(line);
+        ps.setSeparator(' '); //NOI18N
+        String s = ps.nextPair();
+        while (s != null) {
+            if (s.startsWith("-") || s.contains("%classpath")) { //NOI18N
+                s = ps.nextPair();
+                continue;
+            } else if (s.equals(PACKAGE_CLASS_NAME_TOKEN) || s.equals(RUN_MAIN_CLASS_TOKEN) || s.matches("[\\w]+[\\.]{0,1}[\\w\\.]*")) { //NOI18N
+                return s;
+            } else {
+                Logger.getLogger(RunJarPanel.class.getName()).fine("failed splitting main class from=" + line); //NOI18N
+            }
+            s = ps.nextPair();
+        }
+        return ""; //NOI18N
+    }
+    
+    @NonNull
+    public static String splitParams(String line) {
+        int argsIndex = line.indexOf(RunJarStartupArgs.USER_PROGRAM_ARGS_MARKER);
+        if (argsIndex > -1) {
+            return line.substring(argsIndex + RunJarStartupArgs.USER_PROGRAM_ARGS_MARKER.length()).trim();
+        }
+        String main = splitMainClass(line);
+        if (main.isEmpty()) {
+            return "";
+        }
+        int i = line.indexOf(main);
+        if (i > -1) {
+            return line.substring(i + main.length()).trim();
+        }
+        return ""; //NOI18N
+    }
+    /**
+     * Splits the line into sequence of arguments, respects quoting.
+     * @param line the line as a string
+     * @return arguments in an iterable
+     */
+    public static Iterable<String> propertySplitter(String line) {
+        return propertySplitter(line, true);
+    }
+    
+    public static Iterable<String> propertySplitter(String line, boolean outputQuotes) {
+        class SplitIt implements Iterator<String> {
+            private final PropertySplitter spl = new PropertySplitter(line);
+            private String nextPair;
+
+            public SplitIt() {
+                spl.setSeparator(' ');
+                spl.setOutputQuotes(outputQuotes);
+            }
+            
+            @Override
+            public boolean hasNext() {
+                if (nextPair == null) {
+                    nextPair = spl.nextPair();
+                }
+                return nextPair != null;
+            }
+
+            @Override
+            public String next() {
+                String s;
+                if (nextPair == null) {
+                    nextPair = spl.nextPair();
+                }
+                s = nextPair;
+                nextPair = null;
+                if (s != null) {
+                    return s;
+                } else {
+                    throw new NoSuchElementException();
+                }
+            }
+        }
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                return new SplitIt();
+            }
+        };
+    }
+    
+    /**
+     *
+     * @author mkleint
+     */
+    static class PropertySplitter {

Review comment:
        java/maven/src/org/netbeans/modules/maven/customizer/PropertySplitter.java is not deleted. 




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


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

Posted by GitBox <gi...@apache.org>.
JaroslavTulach commented on pull request #2731:
URL: https://github.com/apache/netbeans/pull/2731#issuecomment-775138891


   > The idea is to run the project action using Lookups.executeWith() and supply the lower layers with instructions how to modify 
   > execution environment. @jtulach it may be also possible to use context parameter of ProjectActionProvider.invokeAction() ?
   
   Seems to me passing the arguments via `ProjectActionProvider.invokeAction` would be better, if it supports existing use-cases.
   


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