diff --git a/extexecution/apichanges.xml b/extexecution/apichanges.xml
--- a/extexecution/apichanges.xml
+++ b/extexecution/apichanges.xml
@@ -104,6 +104,7 @@
External Execution API
External Execution Input API
External Execution Input Printing API
+ External Execution Process API
External Execution Startup Arguments API
External Execution SPI
External Execution Process Destroy SPI
@@ -115,7 +116,50 @@
-
+
+ Enhance API and SPI to make it usable as a proxy to native execution
+
+
+
+
+
+
+ There is new SPI for ProcessBuilder
named
+ ProcessBuilderImplementation2
which now implements
+ Lookup.Provider
to be extensible and provides
+ a separate Environment
object represented by
+ the EnvironmentImplementation
in the SPI.
+ The ProcessBuilder
itself allows access to those
+ new features via getLookup()
and
+ getEnvironment()
methods and it now also
+ implements Lookup.Provider
. The utility class
+ ProcessParameters
wraps up the parameters passed
+ to the SPI for process creation. Instances of
+ Environment
API are created via
+ EnvironmentFactory
support class.
+
+
+ There are also new classes extending a Process
+ functionality named ProcessCharset
,
+ ProcessId
and ProcessSignal
allowing
+ client to get the charset, to get ID and to send signal to
+ a process. This might not be always supported and the support
+ may be pre-checked.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Advice to throw UserQuestionException
diff --git a/extexecution/arch.xml b/extexecution/arch.xml
--- a/extexecution/arch.xml
+++ b/extexecution/arch.xml
@@ -67,6 +67,13 @@
org.openide.windows.OutputWriter
. API provides common implementations too.
+ The process returned by default or custom builders may support additional
+ features. To do so the process must be a Lookup.Provider and put
+ the implementation of the feature to lookup. The possible extensions are
+ located in org.netbeans.api.extexecution.process
+ package. The client then use static methods on respective classes in order
+ to use the extended functionality.
+
@@ -232,8 +248,8 @@
In order to do so it will implement
-
- ProcessBuilderImplementation and pass
+
+ ProcessBuilderImplementation2 and pass
ProcessBuilder to its clients. The API instances are created with
help of
@@ -510,9 +526,8 @@
-->
- No known platform dependencies. In future there could be need for native code
- in order to terminate the whole process tree created by execution of external
- process.
+ No known platform dependencies. The platform dependent way of process
+ termination is in a separate implementation module.
diff --git a/extexecution/nbproject/project.xml b/extexecution/nbproject/project.xml
--- a/extexecution/nbproject/project.xml
+++ b/extexecution/nbproject/project.xml
@@ -88,6 +88,7 @@
org.netbeans.api.extexecution
org.netbeans.api.extexecution.print
+ org.netbeans.api.extexecution.process
org.netbeans.api.extexecution.input
org.netbeans.api.extexecution.startup
org.netbeans.spi.extexecution
diff --git a/extexecution/src/org/netbeans/api/extexecution/Environment.java b/extexecution/src/org/netbeans/api/extexecution/Environment.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/Environment.java
@@ -0,0 +1,156 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution;
+
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.extexecution.EnvironmentAccessor;
+import org.netbeans.spi.extexecution.EnvironmentImplementation;
+import org.openide.util.Parameters;
+
+/**
+ * The class that provides an access to environment variables.
+ *
+ * @see ProcessBuilder#getEnvironment()
+ * @see EnvironmentImplementation
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public final class Environment {
+
+ private final EnvironmentImplementation implementation;
+
+ static {
+ EnvironmentAccessor.setDefault(new EnvironmentAccessor() {
+
+ @Override
+ public Environment createEnvironment(EnvironmentImplementation impl) {
+ return new Environment(impl);
+ }
+ });
+ }
+
+ private Environment(EnvironmentImplementation implementation) {
+ this.implementation = implementation;
+ }
+
+ /**
+ * Returns the value of the variable or null
.
+ *
+ * @param name the name of the variable
+ * @return the value of the variable or null
+ */
+ @CheckForNull
+ public String getVariable(@NonNull String name) {
+ Parameters.notNull("name", name);
+
+ return implementation.getVariable(name);
+ }
+
+ /**
+ * Appends a path to a path-like variable. The proper path separator is used
+ * to separate the new value.
+ *
+ * @param name the name of the variable such as for example
+ * PATH
or LD_LIBRARY_PATH
+ * @param value the value (path to append)
+ */
+ public void appendPath(@NonNull String name, @NonNull String value) {
+ Parameters.notNull("name", name);
+ Parameters.notNull("value", value);
+
+ implementation.appendPath(name, value);
+ }
+
+ /**
+ * Prepends a path to a path-like variable. The proper path separator is used
+ * to separate the new value.
+ *
+ * @param name the name of the variable such as for example
+ * PATH
or LD_LIBRARY_PATH
+ * @param value the value (path to prepend)
+ */
+ public void prependPath(@NonNull String name, @NonNull String value) {
+ Parameters.notNull("name", name);
+ Parameters.notNull("value", value);
+
+ implementation.prependPath(name, value);
+ }
+
+ /**
+ * Sets a value for a variable with the given name.
+ *
+ * @param name the name of the variable
+ * @param value the value
+ */
+ public void setVariable(@NonNull String name, @NonNull String value) {
+ Parameters.notNull("name", name);
+ Parameters.notNull("value", value);
+
+ implementation.setVariable(name, value);
+ }
+
+ /**
+ * Removes a variable with the given name. The subsequent call to
+ * {@link #getVariable(java.lang.String)} with the same argument will return
+ * null
.
+ *
+ * @param name the name of the variable
+ */
+ public void removeVariable(@NonNull String name) {
+ Parameters.notNull("name", name);
+
+ implementation.removeVariable(name);
+ }
+
+ /**
+ * Returns all variable names and associated values as a {@link Map}.
+ * Changes to the map are not propagated back to the {@link Environment}.
+ *
+ * @return all variable names and associated values
+ */
+ @NonNull
+ public Map values() {
+ return implementation.values();
+ }
+}
diff --git a/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java b/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java
--- a/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java
+++ b/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java
@@ -52,6 +52,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
+import java.io.Writer;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -66,10 +67,12 @@
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.extexecution.ProcessInputStream;
@@ -79,7 +82,9 @@
import org.netbeans.api.extexecution.input.InputProcessors;
import org.netbeans.api.extexecution.input.InputReaderTask;
import org.netbeans.api.extexecution.input.InputReaders;
+import org.netbeans.api.extexecution.input.LineProcessor;
import org.netbeans.api.extexecution.input.LineProcessors;
+import org.netbeans.api.extexecution.process.ProcessCharset;
import org.openide.util.Cancellable;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
@@ -91,20 +96,20 @@
* Execution service provides the facility to execute the process while
* displaying the output and handling the input.
*
- * It will execute the program with an associated I/O window, with stop and
- * restart buttons. It will also obey various descriptor properties such as
- * whether or not to show a progress bar.
+ * It will execute the program with an associated I/O window, possibly with
+ * stop and restart buttons. It will also obey various descriptor properties
+ * such as whether or not to show a progress bar.
*
* All processes launched by this class are terminated on VM exit (if
* these are not finished or terminated earlier).
*
- * Note that once service is run for the first time, subsequents runs can be
+ * Note that once service is run for the first time, subsequent runs can be
* invoked by the user (rerun button) if it is allowed to do so
* ({@link ExecutionDescriptor#isControllable()}).
*
*
*
- * Sample usage - executing ls command:
+ * Sample usage (ls command):
*
* ExecutionDescriptor descriptor = new ExecutionDescriptor()
* .frontWindow(true).controllable(true);
@@ -114,6 +119,14 @@
* ExecutionService service = ExecutionService.newService(processBuilder, descriptor, "ls command");
* Future<Integer> task = service.run();
*
+ *
+ * Even simpler usage but without displaying output (ls command):
+ *
+ * ExternalProcessBuilder processBuilder = new ExternalProcessBuilder("ls");
+ *
+ * ExecutionService service = ExecutionService.newService(processBuilder, null, null);
+ * Future<Integer> task = service.run();
+ *
*
*
* @author Petr Hejl
@@ -130,6 +143,21 @@
private static final ExecutorService EXECUTOR_SERVICE = new RequestProcessor(ExecutionService.class.getName(), Integer.MAX_VALUE);
+ private static final InputProcessor NULL_PROCESSOR = new InputProcessor() {
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ }
+
+ @Override
+ public void reset() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ };
+
static {
// rerun accessor
RerunAction.Accessor.setDefault(new RerunAction.Accessor() {
@@ -156,16 +184,25 @@
});
}
- private final Callable processCreator;
+ private final Callable extends Process> processCreator;
private final ExecutionDescriptor descriptor;
private final String originalDisplayName;
- private ExecutionService(Callable processCreator, String displayName, ExecutionDescriptor descriptor) {
+ private final Reader processInput;
+
+ private final boolean rerunAllowed;
+
+ private final AtomicInteger runCount = new AtomicInteger();
+
+ private ExecutionService(Callable extends Process> processCreator, String displayName,
+ ExecutionDescriptor descriptor, Reader processInput, boolean rerunAllowed) {
this.processCreator = processCreator;
this.originalDisplayName = displayName;
this.descriptor = descriptor;
+ this.processInput = processInput;
+ this.rerunAllowed = rerunAllowed;
}
/**
@@ -178,24 +215,130 @@
* @return new execution service
*/
@NonNull
- public static ExecutionService newService(@NonNull Callable processCreator,
+ public static ExecutionService newService(@NonNull Callable extends Process> processCreator,
@NonNull ExecutionDescriptor descriptor, @NonNull String displayName) {
- return new ExecutionService(processCreator, displayName, descriptor);
+ return new ExecutionService(processCreator, displayName, descriptor, null, true);
+ }
+
+ /**
+ * Creates new execution service. Service will wrap up the processes
+ * created by processCreator
and will manage them. The service
+ * may not be run more than once otherwise {@link IllegalStateException}
+ * is thrown on {@link #run()}. No UI will be displayed.
+ *
+ * Passed processInput will be implicitly closed when the execution (invoked by
+ * {@link #run()}) is finished. The client should not use the reader passed
+ * as argument anymore or the reader should be designed thread safe.
+ *
+ * @param processCreator callable returning the process to wrap up
+ * @param outputProcessor processor processing lines of standard output;
+ * may be null
+ * @param errorProcessor processor processing lines of standard error
+ * output; may be null
+ * @param processorInput reader providing input for standard input of
+ * the created process. Once passed it either should not be
+ * used anymore or it should be designed to be thread safe.
+ * May be null
.
+ * @return new execution service
+ * @since 1.37
+ */
+ @NonNull
+ public static ExecutionService newService(@NonNull Callable extends Process> processCreator,
+ @NullAllowed final LineProcessor outputProcessor, @NullAllowed final LineProcessor errorProcessor,
+ @NullAllowed final Reader processorInput) {
+ ExecutionDescriptor descriptor = new ExecutionDescriptor()
+ .inputOutput(InputOutput.NULL);
+
+ if (outputProcessor != null) {
+ descriptor = descriptor.outProcessorFactory(new InputProcessorFactory() {
+ @Override
+ public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
+ return InputProcessors.bridge(outputProcessor);
+ }
+ });
+ }
+ if (errorProcessor != null) {
+ descriptor = descriptor.errProcessorFactory(new InputProcessorFactory() {
+ @Override
+ public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
+ return InputProcessors.bridge(errorProcessor);
+ }
+ });
+ }
+
+ return new ExecutionService(processCreator, null, descriptor, processorInput, false);
+ }
+
+ /**
+ * Creates new execution service. Service will wrap up the processes
+ * created by processCreator
and will manage them. The service
+ * may not be run more than once otherwise {@link IllegalStateException}
+ * is thrown on {@link #run()}. No UI will be displayed.
+ *
+ * Passed inputWriter and errorWriter will be implicitly closed when the
+ * execution (invoked by {@link #run()}) is finished. The client either
+ * should not use the passed writers anymore or these should be designed
+ * to be thread safe.
+ *
+ * @param processCreator callable returning the process to wrap up
+ * @param outputWriter where to write standard output of the process. Once
+ * passed it either should not be used anymore or it should
+ * be designed to be thread safe. May be null
.
+ * @param errorWriter where to write standard error output of the process.
+ * Once passed it either should not be used anymore or it should
+ * be designed to be thread safe. May be null
.
+ * @return new execution service
+ * @since 1.37
+ */
+ public static ExecutionService newService(@NonNull Callable extends Process> processCreator,
+ @NullAllowed final Writer outputWriter, @NullAllowed final Writer errorWriter) {
+ ExecutionDescriptor descriptor = new ExecutionDescriptor()
+ .inputOutput(InputOutput.NULL);
+
+ if (outputWriter != null) {
+ descriptor = descriptor.outProcessorFactory(new InputProcessorFactory() {
+ @Override
+ public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
+ return InputProcessors.copying(outputWriter);
+ }
+ });
+ }
+ if (errorWriter != null) {
+ descriptor = descriptor.errProcessorFactory(new InputProcessorFactory() {
+ @Override
+ public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
+ return InputProcessors.copying(errorWriter);
+ }
+ });
+ }
+
+ return new ExecutionService(processCreator, null, descriptor, null, false);
}
/**
* Runs the process described by this service. The call does not block
* and the task is represented by the returned value. Integer returned
* as a result of the {@link Future} is exit code of the process.
+ * The ability to call this method multiple times depends on a way
+ * in which the service has been created. Only service created by
+ * {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)}
+ * may be run more than once.
*
* The output tabs are reused (if caller does not use the custom one,
- * see {@link ExecutionDescriptor#getInputOutput()}) - the tab to reuse
- * (if any) is selected by having the same name and same buttons
- * (control and option). If there is no output tab to reuse new one
- * is opened.
+ * see {@link ExecutionDescriptor#getInputOutput()} or if it is not being
+ * run without output tab, see {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.input.LineProcessor, org.netbeans.api.extexecution.input.LineProcessor, java.io.Reader)})
+ * - the tab to reuse (if any) is selected by having the same name
+ * and same buttons (control and option). If there is no output tab to
+ * reuse new one is opened.
*
- * This method can be invoked multiple times returning the different and
- * unrelated {@link Future}s. On each call Callable<Process>
+ * If the service has been created by {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.input.LineProcessor, org.netbeans.api.extexecution.input.LineProcessor, java.io.Reader)}
+ * this method may be called only once. Subsequent calls will cause an
+ * {@link IllegalStateException} to be thrown.
+ * If the service has been created by {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)}
+ * this method can be invoked multiple times returning the different and
+ * unrelated {@link Future}s.
+ *
+ * On each call Callable<Process>
* passed to {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)}
* is invoked in order to create the process. If the process creation fails
* (throwing an exception) returned Future
will throw
@@ -213,6 +356,10 @@
}
private Future run(InputOutput required) {
+ if (!rerunAllowed && runCount.incrementAndGet() > 1) {
+ throw new IllegalStateException("Run invoked multimple times");
+ }
+
final InputOutputManager.InputOutputData ioData = getInputOutput(required);
final String displayName = ioData.getDisplayName();
@@ -220,9 +367,16 @@
final ProgressHandle handle = createProgressHandle(ioData.getInputOutput(), displayName, cancellable);
final InputOutput io = ioData.getInputOutput();
- final OutputWriter out = io.getOut();
- final OutputWriter err = io.getErr();
- final Reader in = io.getIn();
+ assert processInput == null || io == InputOutput.NULL;
+
+ final Reader in;
+ if (processInput != null) {
+ in = processInput;
+ } else if (descriptor.isInputVisible() && io != InputOutput.NULL) {
+ in = io.getIn();
+ } else {
+ in = null;
+ }
final CountDownLatch finishedLatch = new CountDownLatch(1);
@@ -267,23 +421,37 @@
outStream = new ProcessInputStream(process, process.getInputStream());
errStream = new ProcessInputStream(process, process.getErrorStream());
- executor = Executors.newFixedThreadPool(descriptor.isInputVisible() ? 3 : 2);
+ executor = Executors.newFixedThreadPool(in != null ? 3 : 2);
Charset charset = descriptor.getCharset();
+ Charset inputCharset = charset;
+ Charset outputCharset = charset;
+ Charset errorCharset = charset;
if (charset == null) {
- charset = Charset.defaultCharset();
+ inputCharset = ProcessCharset.getInputCharset(process);
+ outputCharset = ProcessCharset.getOutputCharset(process);
+ errorCharset = ProcessCharset.getErrorCharset(process);
+ }
+ if (inputCharset == null) {
+ inputCharset = Charset.defaultCharset();
+ }
+ if (outputCharset == null) {
+ outputCharset = Charset.defaultCharset();
+ }
+ if (errorCharset == null) {
+ errorCharset = Charset.defaultCharset();
}
tasks.add(InputReaderTask.newDrainingTask(
- InputReaders.forStream(new BufferedInputStream(outStream), charset),
- createOutProcessor(out)));
+ InputReaders.forStream(new BufferedInputStream(outStream), outputCharset),
+ createOutProcessor(io)));
tasks.add(InputReaderTask.newDrainingTask(
- InputReaders.forStream(new BufferedInputStream(errStream), charset),
- createErrProcessor(err)));
- if (descriptor.isInputVisible()) {
+ InputReaders.forStream(new BufferedInputStream(errStream), errorCharset),
+ createErrProcessor(io)));
+ if (in != null) {
tasks.add(InputReaderTask.newTask(
InputReaders.forReader(in),
- createInProcessor(process.getOutputStream())));
+ createInProcessor(process.getOutputStream(), inputCharset)));
}
for (InputReaderTask task : tasks) {
executor.submit(task);
@@ -568,15 +736,20 @@
Mutex.EVENT.readAccess(ui);
}
- private InputProcessor createOutProcessor(OutputWriter writer) {
- LineConvertorFactory convertorFactory = descriptor.getOutConvertorFactory();
+ private InputProcessor createOutProcessor(InputOutput io) {
InputProcessor outProcessor = null;
- if (descriptor.isOutLineBased()) {
- outProcessor = InputProcessors.bridge(LineProcessors.printing(writer,
- convertorFactory != null ? convertorFactory.newLineConvertor() : null, true));
+ if (io != InputOutput.NULL) {
+ LineConvertorFactory convertorFactory = descriptor.getOutConvertorFactory();
+ OutputWriter writer = io.getOut();
+ if (descriptor.isOutLineBased()) {
+ outProcessor = InputProcessors.bridge(LineProcessors.printing(writer,
+ convertorFactory != null ? convertorFactory.newLineConvertor() : null, true));
+ } else {
+ outProcessor = InputProcessors.printing(writer,
+ convertorFactory != null ? convertorFactory.newLineConvertor() : null, true);
+ }
} else {
- outProcessor = InputProcessors.printing(writer,
- convertorFactory != null ? convertorFactory.newLineConvertor() : null, true);
+ outProcessor = NULL_PROCESSOR;
}
InputProcessorFactory descriptorOutFactory = descriptor.getOutProcessorFactory();
@@ -587,15 +760,20 @@
return outProcessor;
}
- private InputProcessor createErrProcessor(OutputWriter writer) {
- LineConvertorFactory convertorFactory = descriptor.getErrConvertorFactory();
+ private InputProcessor createErrProcessor(InputOutput io) {
InputProcessor errProcessor = null;
- if (descriptor.isErrLineBased()) {
- errProcessor = InputProcessors.bridge(LineProcessors.printing(writer,
- convertorFactory != null ? convertorFactory.newLineConvertor() : null, false));
+ if (io != InputOutput.NULL) {
+ LineConvertorFactory convertorFactory = descriptor.getErrConvertorFactory();
+ OutputWriter writer = io.getErr();
+ if (descriptor.isErrLineBased()) {
+ errProcessor = InputProcessors.bridge(LineProcessors.printing(writer,
+ convertorFactory != null ? convertorFactory.newLineConvertor() : null, false));
+ } else {
+ errProcessor = InputProcessors.printing(writer,
+ convertorFactory != null ? convertorFactory.newLineConvertor() : null, false);
+ }
} else {
- errProcessor = InputProcessors.printing(writer,
- convertorFactory != null ? convertorFactory.newLineConvertor() : null, false);
+ errProcessor = NULL_PROCESSOR;
}
InputProcessorFactory descriptorErrFactory = descriptor.getErrProcessorFactory();
@@ -606,8 +784,8 @@
return errProcessor;
}
- private InputProcessor createInProcessor(OutputStream os) {
- return InputProcessors.copying(new OutputStreamWriter(os));
+ private InputProcessor createInProcessor(OutputStream os, Charset charset) {
+ return InputProcessors.copying(new OutputStreamWriter(os, charset));
}
private static class ProgressCancellable implements Cancellable {
diff --git a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java
--- a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java
+++ b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java
@@ -100,6 +100,8 @@
private final Map envVariables = new HashMap();
+ private final boolean emptySystemVariables;
+
/**
* Creates the new builder that will create the process by running
* given executable. Arguments must not be part of the string.
@@ -117,6 +119,7 @@
this.arguments.addAll(builder.arguments);
this.paths.addAll(builder.paths);
this.envVariables.putAll(builder.envVariables);
+ this.emptySystemVariables = builder.emptySystemVariables;
}
/**
@@ -286,7 +289,13 @@
}
Map pbEnv = pb.environment();
- Map env = buildEnvironment(pbEnv);
+ Map env;
+ if (emptySystemVariables) {
+ pbEnv.clear();
+ env = new HashMap();
+ } else {
+ env = buildEnvironment(pbEnv);
+ }
pbEnv.putAll(env);
String uuid = UUID.randomUUID().toString();
pbEnv.put(WrapperProcess.KEY_UUID, uuid);
@@ -297,6 +306,11 @@
return wp;
}
+ ExternalProcessBuilder emptySystemVariables(boolean emptySystemVariables) {
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.emptySystemVariables(emptySystemVariables));
+ }
+
/**
* Logs the given pb
using the given level
.
*
@@ -331,18 +345,7 @@
// Find PATH environment variable - on Windows it can be some other
// case and we should use whatever it has.
- String pathName = "PATH"; // NOI18N
-
- if (Utilities.isWindows()) {
- pathName = "Path"; // NOI18N
-
- for (String key : ret.keySet()) {
- if ("PATH".equals(key.toUpperCase(Locale.ENGLISH))) { // NOI18N
- pathName = key;
- break;
- }
- }
- }
+ String pathName = getPathName(original);
// TODO use StringBuilder
String currentPath = ret.get(pathName);
@@ -362,6 +365,7 @@
return ret;
}
+
// package level for unit testing
List buildArguments() {
if (!Utilities.isWindows()) {
@@ -378,6 +382,44 @@
return result;
}
+ static void putPath(File path, String pathName, boolean prepend, Map current) {
+ String currentPath = current.get(pathName);
+
+ if (currentPath == null) {
+ currentPath = "";
+ }
+
+ if (prepend) {
+ currentPath = path.getAbsolutePath().replace(" ", "\\ ") //NOI18N
+ + File.pathSeparator + currentPath;
+ } else {
+ currentPath = currentPath + File.pathSeparator
+ + path.getAbsolutePath().replace(" ", "\\ "); //NOI18N
+ }
+
+ if (!"".equals(currentPath.trim())) {
+ current.put(pathName, currentPath);
+ }
+ }
+
+ static String getPathName(Map systemEnv) {
+ // Find PATH environment variable - on Windows it can be some other
+ // case and we should use whatever it has.
+ String pathName = "PATH"; // NOI18N
+
+ if (Utilities.isWindows()) {
+ pathName = "Path"; // NOI18N
+
+ for (String keySystem : systemEnv.keySet()) {
+ if ("PATH".equals(keySystem.toUpperCase(Locale.ENGLISH))) { // NOI18N
+ pathName = keySystem;
+ break;
+ }
+ }
+ }
+ return pathName;
+ }
+
private static String escapeString(String s) {
if (s.length() == 0) {
return "\"\""; // NOI18N
@@ -475,6 +517,8 @@
private Map envVariables = new HashMap();
+ private boolean emptySystemVariables;
+
public BuilderData(String executable) {
this.executable = executable;
}
@@ -486,6 +530,7 @@
this.arguments.addAll(builder.arguments);
this.paths.addAll(builder.paths);
this.envVariables.putAll(builder.envVariables);
+ this.emptySystemVariables = builder.emptySystemVariables;
}
public BuilderData workingDirectory(File workingDirectory) {
@@ -521,6 +566,11 @@
envVariables.put(name, value);
return this;
}
+
+ public BuilderData emptySystemVariables(boolean emptySystemVariables) {
+ this.emptySystemVariables = emptySystemVariables;
+ return this;
+ }
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java b/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java
--- a/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java
+++ b/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java
@@ -46,12 +46,19 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.modules.extexecution.ProcessBuilderAccessor;
+import org.netbeans.modules.extexecution.ProcessParametersAccessor;
+import org.netbeans.spi.extexecution.EnvironmentFactory;
+import org.netbeans.spi.extexecution.EnvironmentImplementation;
import org.netbeans.spi.extexecution.ProcessBuilderImplementation;
+import org.netbeans.spi.extexecution.ProcessBuilderImplementation2;
+import org.netbeans.spi.extexecution.ProcessParameters;
+import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.openide.util.UserQuestionException;
@@ -68,35 +75,49 @@
* for creating the local machine OS processes.
*
* Thread safety of this class depends on thread safety of
- * the {@link ProcessBuilderImplementation} the class is using. If it is thread
+ * the implementation class.
+ *
+ * If the {@link ProcessBuilderImplementation} is used and it is thread
* safe (if possible the implementation should be even stateless) this class
* is thread safe as well.
+ *
+ * If the {@link ProcessBuilderImplementation2} is used and it is (including
+ * {@link EnvironmentImplementation}) thread safe and does not have any mutable
+ * configuration accessible via {@link ProcessBuilderImplementation2#getLookup()}
+ * it is thread safe as well. Otherwise it is not thread safe.
+ *
+ * The synchronization mechanism use in this object is the {@link ProcessBuilderImplementation}
+ * or {@link ProcessBuilderImplementation2} object monitor.
*
* @author Petr Hejl
* @since 1.28
*/
-public final class ProcessBuilder implements Callable {
+public final class ProcessBuilder implements Callable, Lookup.Provider {
private final ProcessBuilderImplementation implementation;
+ private final ProcessBuilderImplementation2 implementation2;
+
+ private final Object lock;
+
private final String description;
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private String executable;
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private String workingDirectory;
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private List arguments = new ArrayList();
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private List paths = new ArrayList();
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private Map envVariables = new HashMap();
- /**GuardedBy("this")*/
+ /**GuardedBy("lock")*/
private boolean redirectErrorStream;
static {
@@ -106,12 +127,32 @@
public ProcessBuilder createProcessBuilder(ProcessBuilderImplementation impl, String description) {
return new ProcessBuilder(impl, description);
}
+
+ @Override
+ public ProcessBuilder createProcessBuilder(ProcessBuilderImplementation2 impl, String description) {
+ return new ProcessBuilder(impl, description);
+ }
});
}
private ProcessBuilder(ProcessBuilderImplementation implementation, String description) {
+ this(implementation, null, description);
+ }
+
+ private ProcessBuilder(ProcessBuilderImplementation2 implementation2, String description) {
+ this(null, implementation2, description);
+ }
+
+ private ProcessBuilder(ProcessBuilderImplementation implementation, ProcessBuilderImplementation2 implementation2, String description) {
+ assert implementation == null || implementation2 == null;
this.implementation = implementation;
+ this.implementation2 = implementation2;
this.description = description;
+ if (implementation != null) {
+ lock = implementation;
+ } else {
+ lock = implementation2;
+ }
}
/**
@@ -122,7 +163,7 @@
* machine
*/
public static ProcessBuilder getLocal() {
- return new ProcessBuilder(new LocalProcessFactory(),
+ return new ProcessBuilder(new LocalProcessBuilder(),
NbBundle.getMessage(ProcessBuilder.class, "LocalProcessBuilder"));
}
@@ -146,7 +187,7 @@
public void setExecutable(@NonNull String executable) {
Parameters.notNull("executable", executable);
- synchronized (this) {
+ synchronized (lock) {
this.executable = executable;
}
}
@@ -158,7 +199,7 @@
* @param workingDirectory the working directory of the process
*/
public void setWorkingDirectory(@NullAllowed String workingDirectory) {
- synchronized (this) {
+ synchronized (lock) {
this.workingDirectory = workingDirectory;
}
}
@@ -172,7 +213,7 @@
public void setArguments(@NonNull List arguments) {
Parameters.notNull("arguments", arguments);
- synchronized (this) {
+ synchronized (lock) {
this.arguments.clear();
this.arguments.addAll(arguments);
}
@@ -184,11 +225,20 @@
* exception of PATH
possibly configured by {@link #setPaths(java.util.List)}.
*
* @param envVariables the environment variables for the process
+ * @deprecated use {@link Environment#setVariable(java.lang.String, java.lang.String)}
+ * on object received by {@link #getEnvironment()} call
*/
public void setEnvironmentVariables(@NonNull Map envVariables) {
Parameters.notNull("envVariables", envVariables);
- synchronized (this) {
+ if (implementation2 != null) {
+ for (Map.Entry entry : envVariables.entrySet()) {
+ implementation2.getEnvironment().setVariable(entry.getKey(), entry.getValue());
+ }
+ return;
+ }
+
+ synchronized (lock) {
this.envVariables.clear();
this.envVariables.putAll(envVariables);
}
@@ -196,15 +246,32 @@
/**
* Sets the additional paths to be included in PATH
environment
- * variable for the process.
+ * variable for the process. The paths are prepended to the PATH variable
+ * one by one as specified in the list so in fact the last path in
+ * the list will be the first one in the PATH variable.
+ *
+ * This method is semi-deprecated. If {@link #getEnvironment()} is not
+ * null
this call adds paths one by one by call to
+ * {@link Environment#prependPath(java.lang.String, java.lang.String)}
+ * on returned object.
*
* @param paths the additional paths to be included in PATH
* environment variable
+ * @deprecated use {@link Environment#prependPath(java.lang.String, java.lang.String)}
+ * on object received by {@link #getEnvironment()} call, with
+ * the first argument being PATH
*/
public void setPaths(@NonNull List paths) {
Parameters.notNull("paths", paths);
- synchronized (this) {
+ if (implementation2 != null) {
+ for (String path : paths) {
+ implementation2.getEnvironment().prependPath("PATH", path); // NOI18N
+ }
+ return;
+ }
+
+ synchronized (lock) {
this.paths.clear();
this.paths.addAll(paths);
}
@@ -218,12 +285,86 @@
* @param redirectErrorStream the error stream redirection
*/
public void setRedirectErrorStream(boolean redirectErrorStream) {
- synchronized (this) {
+ synchronized (lock) {
this.redirectErrorStream = redirectErrorStream;
}
}
/**
+ * Returns the object for environment variables manipulation.
+ *
+ * @return the object for environment variables manipulation
+ * @since 1.37
+ */
+ @NonNull
+ public Environment getEnvironment() {
+ if (implementation2 != null) {
+ return implementation2.getEnvironment();
+ }
+ return EnvironmentFactory.createEnvironment(new EnvironmentImplementation() {
+
+ @Override
+ public void prependPath(String name, String value) {
+ if (!name.equalsIgnoreCase("PATH")) { // NOI18N
+ throw new UnsupportedOperationException("The deprecated implementation "
+ + implementation.getClass().getName() + "does not support this method for a non PATH variable.");
+ }
+ synchronized (lock) {
+ paths.add(0, value);
+ }
+ }
+
+ @Override
+ public void setVariable(String name, String value) {
+ synchronized (lock) {
+ envVariables.put(name, value);
+ }
+ }
+
+ @Override
+ public String getVariable(String name) {
+ throw new UnsupportedOperationException("The deprecated implementation "
+ + implementation.getClass().getName() + "does not support this method.");
+ }
+
+ @Override
+ public void appendPath(String name, String value) {
+ throw new UnsupportedOperationException("The deprecated implementation "
+ + implementation.getClass().getName() + "does not support this method.");
+ }
+
+ @Override
+ public void removeVariable(String name) {
+ throw new UnsupportedOperationException("The deprecated implementation "
+ + implementation.getClass().getName() + "does not support this method.");
+ }
+
+ @Override
+ public Map values() {
+ throw new UnsupportedOperationException("The deprecated implementation "
+ + implementation.getClass().getName() + "does not support this method.");
+ }
+ });
+ }
+
+ /**
+ * Returns the associated {@link Lookup}. Extension point provided by
+ * {@link ProcessBuilderImplementation2}.
+ *
+ * @return the associated {@link Lookup}.
+ * @see ProcessBuilderImplementation2#getLookup()
+ * @since 1.37
+ */
+ @Override
+ public Lookup getLookup() {
+ if (implementation2 != null) {
+ return implementation2.getLookup();
+ }
+ return Lookup.EMPTY;
+ }
+
+
+ /**
* Creates the new {@link Process} based on the properties configured
* in this builder.
*
@@ -232,7 +373,7 @@
*
* Since version 1.35 implementors of this method are advised to throw
* a {@link UserQuestionException} in case the execution cannot be
- * performed and requires additional user confirmation, or configuration.
+ * performed and requires additional user confirmation, or configuration.
* Callers of this method may check for this exception and handle it
* appropriately.
*
@@ -248,52 +389,168 @@
@NonNull
@Override
public Process call() throws IOException {
- String currentExecutable = null;
- String currentWorkingDirectory = null;
+ String currentExecutable;
+ String currentWorkingDirectory;
List currentArguments = new ArrayList();
List currentPaths = new ArrayList();
- Map currentEnvVariables = new HashMap();
- boolean currentRedirectErrorStream = false;
+ Map currentVariables = new HashMap();
+ boolean currentRedirectErrorStream;
- synchronized (this) {
+ synchronized (lock) {
currentExecutable = executable;
currentWorkingDirectory = workingDirectory;
currentArguments.addAll(arguments);
currentPaths.addAll(paths);
- currentEnvVariables.putAll(envVariables);
currentRedirectErrorStream = redirectErrorStream;
+ if (implementation2 != null) {
+ currentVariables.putAll(getEnvironment().values());
+ } else {
+ currentVariables.putAll(envVariables);
+ }
}
if (currentExecutable == null) {
throw new IllegalStateException("The executable has not been configured");
}
+ if (implementation2 != null) {
+ ProcessParameters params = ProcessParametersAccessor.getDefault().createProcessParameters(
+ currentExecutable, currentWorkingDirectory, currentArguments,
+ currentRedirectErrorStream, currentVariables);
+ return implementation2.createProcess(params);
+ }
+
return implementation.createProcess(currentExecutable, currentWorkingDirectory, currentArguments,
- currentPaths, currentEnvVariables, currentRedirectErrorStream);
+ currentPaths, currentVariables, currentRedirectErrorStream);
}
- private static class LocalProcessFactory implements ProcessBuilderImplementation {
+ /**
+ * Marks an object from which it is possible to get a {@link ProcessBuilder}.
+ *
+ * @since 1.37
+ */
+ public static interface Provider {
+
+ /**
+ * Returns the {@link ProcessBuilder} for the object.
+ *
+ * @return the {@link ProcessBuilder} for the object
+ * @throws IOException if there was a problem with the provision
+ */
+ ProcessBuilder getProcessBuilder() throws IOException;
+
+ }
+
+ private static class LocalProcessBuilder implements ProcessBuilderImplementation2 {
+
+ private final Environment environment = EnvironmentFactory.createEnvironment(
+ new LocalEnvironment(this, System.getenv()));
@Override
- public Process createProcess(String executable, String workingDirectory, List arguments,
- List paths, Map environment, boolean redirectErrorStream) throws IOException {
+ public Environment getEnvironment() {
+ return environment;
+ }
- ExternalProcessBuilder builder = new ExternalProcessBuilder(executable);
- if (workingDirectory != null) {
- builder = builder.workingDirectory(new File(workingDirectory));
+ @Override
+ public Lookup getLookup() {
+ return Lookup.EMPTY;
+ }
+
+ @Override
+ public Process createProcess(ProcessParameters parameters) throws IOException {
+ ExternalProcessBuilder builder = new ExternalProcessBuilder(parameters.getExecutable());
+ String workingDir = parameters.getWorkingDirectory();
+ if (workingDir != null) {
+ builder = builder.workingDirectory(new File(workingDir));
}
- for (String argument : arguments) {
+ for (String argument : parameters.getArguments()) {
builder = builder.addArgument(argument);
}
- for (String path : paths) {
- builder = builder.prependPath(new File(path));
- }
- for (Map.Entry entry : environment.entrySet()) {
+ builder = builder.redirectErrorStream(parameters.isRedirectErrorStream());
+
+ builder = builder.emptySystemVariables(true);
+ for (Map.Entry entry : parameters.getEnvironmentVariables().entrySet()) {
builder = builder.addEnvironmentVariable(entry.getKey(), entry.getValue());
}
- builder = builder.redirectErrorStream(redirectErrorStream);
return builder.call();
}
}
+
+ private static class LocalEnvironment implements EnvironmentImplementation {
+
+ private final LocalProcessBuilder builder;
+
+ private final Map systemEnvironment;
+
+ private final String pathName;
+
+ public LocalEnvironment(LocalProcessBuilder builder, Map systemEnvironment) {
+ this.builder = builder;
+ this.systemEnvironment = new HashMap(systemEnvironment);
+ this.pathName = ExternalProcessBuilder.getPathName(systemEnvironment);
+ }
+
+ @Override
+ public String getVariable(String name) {
+ synchronized (builder) {
+ if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N
+ return systemEnvironment.get(pathName);
+ } else {
+ return systemEnvironment.get(name);
+ }
+ }
+ }
+
+ @Override
+ public void appendPath(String name, String value) {
+ putPath(name, value, false);
+ }
+
+ @Override
+ public void prependPath(String name, String value) {
+ putPath(name, value, true);
+ }
+
+ @Override
+ public void setVariable(String name, String value) {
+ synchronized (builder) {
+ if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N
+ systemEnvironment.put(pathName, value);
+ } else {
+ systemEnvironment.put(name, value);
+ }
+ }
+ }
+
+ @Override
+ public void removeVariable(String name) {
+ synchronized (builder) {
+ if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N
+ systemEnvironment.remove(pathName);
+ } else {
+ systemEnvironment.remove(name);
+ }
+ }
+ }
+
+ @Override
+ public Map values() {
+ synchronized (builder) {
+ return new HashMap(systemEnvironment);
+ }
+ }
+
+ private void putPath(String name, String value, boolean prepend) {
+ synchronized (builder) {
+ if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N
+ ExternalProcessBuilder.putPath(new File(value), pathName,
+ prepend, systemEnvironment);
+ } else {
+ ExternalProcessBuilder.putPath(new File(value), name,
+ prepend, systemEnvironment);
+ }
+ }
+ }
+ }
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java b/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java
@@ -46,6 +46,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.io.StringReader;
import java.nio.charset.Charset;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
@@ -82,6 +83,12 @@
*/
@NonNull
public static InputReader forReader(@NonNull Reader reader) {
+ if (reader instanceof StringReader) {
+ // unfortunatelly the string reader is always
+ // ready (isReady() returns true) which I would consider a bug
+ // when end of string is reached
+ return new DefaultInputReader(reader, false);
+ }
return new DefaultInputReader(reader, true);
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java
@@ -0,0 +1,169 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import java.nio.charset.Charset;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.Lookup;
+import org.openide.util.Parameters;
+
+/**
+ * This class allows client to get input and output charset of
+ * the {@link Process}. Though the process charsets might not be always
+ * supported.
+ *
+ * If the {@link Process} implementation wants to support charsets it has to
+ * implement {@link Lookup.Provider} and there has to be instance of this class
+ * in the returned {@link Lookup}. The functionality itself is implemented in
+ * methods {@link #getInputCharset()} and {@link #getOutputCharset()}.
+ *
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public abstract class ProcessCharset {
+
+ private static ProcessCharset find(Process process) {
+ if (process instanceof Lookup.Provider) {
+ Lookup.Provider p = (Lookup.Provider) process;
+ return p.getLookup().lookup(ProcessCharset.class);
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether the getting of charsets of the {@link Process} is supported.
+ * If the return value is false
methods
+ * {@link #getInputCharset(java.lang.Process)} and {@link #getOutputCharset(java.lang.Process)}
+ * will return null
for sure.
+ *
+ * @param process the process to check
+ * @return true
if process charsets can be received false
+ * otherwise
+ */
+ public static boolean isSupported(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ return find(process) != null;
+ }
+
+ /**
+ * Returns the input charset of the process if supported. In case it is not
+ * supported or the process can't determine the charset null
is
+ * returned.
+ *
+ * @param process the process
+ * @return the input charset of the process or null
+ */
+ @CheckForNull
+ public static Charset getInputCharset(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ ProcessCharset processCharset = find(process);
+ if (processCharset != null) {
+ return processCharset.getInputCharset();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the output charset of the process if supported. In case it is not
+ * supported or the process can't determine the charset null
is
+ * returned.
+ *
+ * @param process the process
+ * @return the output charset of the process or null
+ */
+ @CheckForNull
+ public static Charset getOutputCharset(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ ProcessCharset processCharset = find(process);
+ if (processCharset != null) {
+ return processCharset.getOutputCharset();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the error output charset of the process if supported. In case it
+ * is not supported or the process can't determine the charset null
+ * is returned.
+ *
+ * @param process the process
+ * @return the error output charset of the process or null
+ */
+ @CheckForNull
+ public static Charset getErrorCharset(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ ProcessCharset processCharset = find(process);
+ if (processCharset != null) {
+ return processCharset.getErrorCharset();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the input charset of a process or null
.
+ *
+ * @return input charset of a process or null
+ */
+ @CheckForNull
+ protected abstract Charset getInputCharset();
+
+ /**
+ * Returns the output charset of a process or null
.
+ *
+ * @return output charset of a process or null
+ */
+ @CheckForNull
+ protected abstract Charset getOutputCharset();
+
+ /**
+ * Returns the error output charset of a process or null
.
+ *
+ * @return error output charset of a process or null
+ */
+ @CheckForNull
+ protected abstract Charset getErrorCharset();
+}
diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java
@@ -0,0 +1,111 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.Lookup;
+import org.openide.util.Parameters;
+
+/**
+ * This class allows client to request ID of the {@link Process}. Though
+ * the process ID might not be always supported.
+ *
+ * If the {@link Process} implementation wants to support the ID it has to
+ * implement {@link Lookup.Provider} and there has to be instance of this class
+ * in the returned {@link Lookup}. The functionality itself is implemented in
+ * method {@link #getId()}.
+ *
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public abstract class ProcessId {
+
+ private static ProcessId find(Process process) {
+ if (process instanceof Lookup.Provider) {
+ Lookup.Provider p = (Lookup.Provider) process;
+ return p.getLookup().lookup(ProcessId.class);
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether the getting of the ID of the {@link Process} is supported.
+ * If the return value is false
the {@link #getId(java.lang.Process)}
+ * will return null
for sure.
+ *
+ * @param process the process to check
+ * @return true
if the ID can be received false
+ * otherwise
+ */
+ public static boolean isSupported(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ return find(process) != null;
+ }
+
+ /**
+ * Returns the ID of the process if supported. In case it is not supported
+ * or the process can't determine its ID null
is returned.
+ *
+ * @param process the process
+ * @return the process ID or null
+ */
+ @CheckForNull
+ public static Integer getId(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ ProcessId processId = find(process);
+ if (processId != null) {
+ return processId.getId();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the ID of a process or null
.
+ *
+ * @return ID of a process or null
+ */
+ @CheckForNull
+ protected abstract Integer getId();
+}
diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java
@@ -0,0 +1,181 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import java.io.IOException;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.Lookup;
+import org.openide.util.Parameters;
+
+/**
+ * This class allows client send a signal to the {@link Process} or to whole
+ * its group. Though signal sending might not be always supported.
+ *
+ * If the {@link Process} implementation wants to support the signal sending
+ * it has to implement {@link Lookup.Provider} and there has to be instance
+ * of this class in the returned {@link Lookup}. The functionality itself is
+ * implemented in methods {@link #signal(org.netbeans.api.extexecution.process.ProcessSignal.Signal)}
+ * and {@link #signalGroup(org.netbeans.api.extexecution.process.ProcessSignal.Signal)}.
+ *
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public abstract class ProcessSignal {
+
+ private static ProcessSignal find(Process process) {
+ if (process instanceof Lookup.Provider) {
+ Lookup.Provider p = (Lookup.Provider) process;
+ return p.getLookup().lookup(ProcessSignal.class);
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether the signal sending is supported for the {@link Process}.
+ * If the return value is false
the signal operations are
+ * effectively noops.
+ *
+ * @param process the process to check
+ * @return true
if the signals can be send false
+ * otherwise
+ */
+ public static boolean isSupported(@NonNull Process process) {
+ Parameters.notNull("process", process);
+
+ return find(process) != null;
+ }
+
+ /**
+ * Sends a signal to the process. If the signal sending is not supported
+ * this is effectively noop.
+ *
+ * @param process the process where to send signal
+ * @param signal the signal to send
+ * @throws IOException if problem with signal sending occurs
+ */
+ public static void signal(@NonNull Process process, @NonNull Signal signal) throws IOException {
+ Parameters.notNull("process", process);
+ Parameters.notNull("signal", signal);
+
+ ProcessSignal processSignal = find(process);
+ if (processSignal != null) {
+ processSignal.signal(signal);
+ }
+ }
+
+ /**
+ * Sends a signal to the group the process belongs to. If the signal sending
+ * is not supported this is effectively noop.
+ *
+ * @param process the process marking the group where to send signal
+ * @param signal the signal to send
+ * @throws IOException if problem with signal sending occurs
+ */
+ public static void signalGroup(@NonNull Process process, @NonNull Signal signal) throws IOException {
+ Parameters.notNull("process", process);
+ Parameters.notNull("signal", signal);
+
+ ProcessSignal processSignal = find(process);
+ if (processSignal != null) {
+ processSignal.signalGroup(signal);
+ }
+ }
+
+ /**
+ * Sends a given signal to the process.
+ *
+ * @param signal the signal to send
+ * @throws IOException if problem with signal sending occurs
+ */
+ protected abstract void signal(@NonNull Signal signal) throws IOException;
+
+ /**
+ * Sends a given signal to the group the process belongs to.
+ *
+ * @param signal the signal to send
+ * @throws IOException if problem with signal sending occurs
+ */
+ protected abstract void signalGroup(@NonNull Signal signal) throws IOException;
+
+ public enum Signal {
+
+ NULL,
+ SIGHUP,
+ SIGINT,
+ SIGQUIT,
+ SIGILL,
+ SIGTRAP,
+ SIGABRT,
+ SIGEMT,
+ SIGFPE,
+ SIGKILL,
+ SIGBUS,
+ SIGSEGV,
+ SIGSYS,
+ SIGPIPE,
+ SIGALRM,
+ SIGTERM,
+ SIGUSR1,
+ SIGUSR2,
+ SIGCHLD,
+ SIGPWR,
+ SIGWINCH,
+ SIGURG,
+ SIGPOLL,
+ SIGSTOP,
+ SIGTSTP,
+ SIGCONT,
+ SIGTTIN,
+ SIGTTOU,
+ SIGVTALRM,
+ SIGPROF,
+ SIGXCPU,
+ SIGWAITING,
+ SIGLWP,
+ SIGFREEZE,
+ SIGTHAW,
+ SIGCANCEL,
+ SIGLOST,
+ SIGXRES,
+ SIGJVM1;
+ }
+}
diff --git a/extexecution/src/org/netbeans/api/extexecution/process/package-info.java b/extexecution/src/org/netbeans/api/extexecution/process/package-info.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/process/package-info.java
@@ -0,0 +1,50 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ *
+ * The Original Software is NetBeans. The Initial Developer of the Original
+ * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ */
+
+/**
+ * The API supporting enhanced functionality for
+ * a {@link java.lang.Process}.
+ */
+package org.netbeans.api.extexecution.process;
+
diff --git a/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java
@@ -0,0 +1,81 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.extexecution;
+
+import org.netbeans.api.extexecution.Environment;
+import org.netbeans.spi.extexecution.EnvironmentImplementation;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public abstract class EnvironmentAccessor {
+
+ private static volatile EnvironmentAccessor DEFAULT;
+
+ public static EnvironmentAccessor getDefault() {
+ EnvironmentAccessor a = DEFAULT;
+ if (a != null) {
+ return a;
+ }
+
+ // invokes static initializer of Environment.class
+ // that will assign value to the DEFAULT field above
+ Class c = Environment.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException ex) {
+ assert false : ex;
+ }
+ return DEFAULT;
+ }
+
+ public static void setDefault(EnvironmentAccessor accessor) {
+ if (DEFAULT != null) {
+ throw new IllegalStateException();
+ }
+
+ DEFAULT = accessor;
+ }
+
+ public abstract Environment createEnvironment(EnvironmentImplementation impl);
+}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java
--- a/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java
+++ b/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java
@@ -42,6 +42,7 @@
package org.netbeans.modules.extexecution;
import org.netbeans.spi.extexecution.ProcessBuilderImplementation;
+import org.netbeans.spi.extexecution.ProcessBuilderImplementation2;
/**
*
@@ -78,4 +79,7 @@
public abstract org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder(
ProcessBuilderImplementation impl, String description);
+
+ public abstract org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder(
+ ProcessBuilderImplementation2 impl, String description);
}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.java
@@ -0,0 +1,84 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.extexecution;
+
+import java.util.List;
+import java.util.Map;
+import org.netbeans.spi.extexecution.ProcessParameters;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public abstract class ProcessParametersAccessor {
+
+ private static volatile ProcessParametersAccessor DEFAULT;
+
+ public static ProcessParametersAccessor getDefault() {
+ ProcessParametersAccessor a = DEFAULT;
+ if (a != null) {
+ return a;
+ }
+
+ // invokes static initializer of ProcessParameters.class
+ // that will assign value to the DEFAULT field above
+ Class c = ProcessParameters.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException ex) {
+ assert false : ex;
+ }
+ return DEFAULT;
+ }
+
+ public static void setDefault(ProcessParametersAccessor accessor) {
+ if (DEFAULT != null) {
+ throw new IllegalStateException();
+ }
+
+ DEFAULT = accessor;
+ }
+
+ public abstract ProcessParameters createProcessParameters(String executable,
+ String workingDirectory, List arguments, boolean redirectErrorStream,
+ Map environmentVariables);
+}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java
@@ -0,0 +1,69 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.extexecution;
+
+import org.netbeans.api.extexecution.Environment;
+import org.netbeans.modules.extexecution.EnvironmentAccessor;
+
+/**
+ * The factory allowing SPI implementors of {@link EnvironmentImplementation}
+ * to create its API instances {@link Environment}.
+ *
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public class EnvironmentFactory {
+
+ private EnvironmentFactory() {
+ super();
+ }
+
+ /**
+ * Creates the instance of {@link Environment} from its SPI representation.
+ *
+ * @param impl SPI representation
+ * @return the API instance
+ */
+ public static Environment createEnvironment(EnvironmentImplementation impl) {
+ return EnvironmentAccessor.getDefault().createEnvironment(impl);
+ }
+}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.java b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.java
@@ -0,0 +1,114 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.extexecution;
+
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.Environment;
+
+/**
+ * The interface representing the implementation
+ * of {@link org.netbeans.api.extexecution.Environment}.
+ *
+ * @see Environment
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public interface EnvironmentImplementation {
+
+ /**
+ * Returns the value of the variable or null
.
+ *
+ * @param name the name of the variable
+ * @return the value of the variable or null
+ */
+ @CheckForNull
+ String getVariable(@NonNull String name);
+
+ /**
+ * Appends a path to a path-like variable. The proper path separator should
+ * be used to separate the new value.
+ *
+ * @param name the name of the variable such as for example
+ * PATH
or LD_LIBRARY_PATH
+ * @param value the value (path to append)
+ */
+ void appendPath(@NonNull String name, @NonNull String value);
+
+ /**
+ * Prepends a path to a path-like variable. The proper path separator should
+ * be used to separate the new value.
+ *
+ * @param name the name of the variable such as for example
+ * PATH
or LD_LIBRARY_PATH
+ * @param value the value (path to prepend)
+ */
+ void prependPath(@NonNull String name, @NonNull String value);
+
+ /**
+ * Sets a value for a variable with the given name.
+ *
+ * @param name the name of the variable
+ * @param value the value
+ */
+ void setVariable(@NonNull String name, @NonNull String value);
+
+ /**
+ * Removes a variable with the given name. The subsequent call to
+ * {@link #getVariable(java.lang.String)} with the same argument should
+ * return null
.
+ *
+ * @param name the name of the variable
+ */
+ void removeVariable(@NonNull String name);
+
+ /**
+ * Returns all variable names and associated values as a {@link Map}.
+ * Changes to the map must not be propagated back to the {@link Environment}.
+ *
+ * @return all variable names and associated values
+ */
+ @NonNull
+ Map values();
+
+}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java
--- a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java
+++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java
@@ -63,9 +63,24 @@
* @param impl SPI representation
* @param description human readable description of the builder
* @return the API instance
+ * @deprecated use {@link #createProcessBuilder(org.netbeans.spi.extexecution.ProcessBuilderImplementation2, java.lang.String)}
*/
public static org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder(
ProcessBuilderImplementation impl, String description) {
return ProcessBuilderAccessor.getDefault().createProcessBuilder(impl, description);
}
+
+ /**
+ * Creates the instance of {@link org.netbeans.api.extexecution.ProcessBuilder}
+ * from its SPI representation.
+ *
+ * @param impl SPI representation
+ * @param description human readable description of the builder
+ * @return the API instance
+ * @since 1.37
+ */
+ public static org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder(
+ ProcessBuilderImplementation2 impl, String description) {
+ return ProcessBuilderAccessor.getDefault().createProcessBuilder(impl, description);
+ }
}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java
--- a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java
+++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java
@@ -62,6 +62,7 @@
* @see org.netbeans.api.extexecution.ProcessBuilder
* @author Petr Hejl
* @since 1.28
+ * @deprecated use {@link ProcessBuilderImplementation2}
*/
public interface ProcessBuilderImplementation {
@@ -80,7 +81,7 @@
* the process should be redirected to standard output stream
* @return a process created with specified parameters and environment
* configuration
- * @throws IOException IOException if the process could not be created
+ * @throws IOException if the process could not be created
* @throws UserQuestionException in case there is a need to interact with
* user, don't be afraid to throw a subclass of
* {@link UserQuestionException} with overriden {@link UserQuestionException#confirmed()}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java
@@ -0,0 +1,93 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.extexecution;
+
+import java.io.IOException;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.Environment;
+import org.openide.util.Lookup;
+
+/**
+ * The interface representing the implementation
+ * of {@link org.netbeans.api.extexecution.ProcessBuilder}.
+ *
+ * @see org.netbeans.api.extexecution.ProcessBuilder
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public interface ProcessBuilderImplementation2 extends Lookup.Provider {
+
+ /**
+ * Returns the object for environment variables manipulation.
+ *
+ * @return the object for environment variables manipulation
+ */
+ @NonNull
+ Environment getEnvironment();
+
+ /**
+ * Provides an extension point to the implementors. One may enhance the
+ * functionality of {@link org.netbeans.api.extexecution.ProcessBuilder}
+ * by this as the content of this {@link Lookup} is included in
+ * {@link org.netbeans.api.extexecution.ProcessBuilder#getLookup()}
+ *
+ * @return a lookup providing an extension point
+ */
+ @Override
+ Lookup getLookup();
+
+ /**
+ * Creates a process using the specified parameters.
+ *
+ * The environment variables stored in parameters are acquired by call to
+ * {@link Environment#values()}. So if the implementation does not aim to be
+ * or can't thread safe it may check or use the {@link Environment}
+ * directly.
+ *
+ * @param parameters the instance describing the process parameters
+ * @return a process created with specified parameters
+ * @throws IOException if the process could not be created
+ */
+ @NonNull
+ Process createProcess(@NonNull ProcessParameters parameters) throws IOException;
+
+}
diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java
@@ -0,0 +1,142 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.extexecution;
+
+import java.util.List;
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.extexecution.ProcessParametersAccessor;
+
+/**
+ * The parameters configured for process creation.
+ *
+ * @see ProcessBuilderImplementation2
+ * @author Petr Hejl
+ * @since 1.37
+ */
+public final class ProcessParameters {
+
+ private final String executable;
+
+ private final String workingDirectory;
+
+ private final List arguments;
+
+ private final boolean redirectErrorStream;
+
+ private final Map environmentVariables;
+
+ static {
+ ProcessParametersAccessor.setDefault(new ProcessParametersAccessor() {
+
+ @Override
+ public ProcessParameters createProcessParameters(String executable, String workingDirectory,
+ List arguments, boolean redirectErrorStream, Map environmentVariables) {
+ return new ProcessParameters(executable, workingDirectory, arguments,
+ redirectErrorStream, environmentVariables);
+ }
+ });
+ }
+
+ private ProcessParameters(String executable, String workingDirectory, List arguments,
+ boolean redirectErrorStream, Map environmentVariables) {
+ this.executable = executable;
+ this.workingDirectory = workingDirectory;
+ this.arguments = arguments;
+ this.redirectErrorStream = redirectErrorStream;
+ this.environmentVariables = environmentVariables;
+ }
+
+ /**
+ * Returns the configured executable.
+ *
+ * @return the configured executable
+ */
+ @NonNull
+ public String getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Returns the configured working directory or null
in case it
+ * was not configured.
+ *
+ * @return the configured working directory or null
in case it
+ * was not configured
+ */
+ @CheckForNull
+ public String getWorkingDirectory() {
+ return workingDirectory;
+ }
+
+ /**
+ * Returns the arguments configured for the process.
+ *
+ * @return the arguments configured for the process
+ */
+ @NonNull
+ public List getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Returns true
if standard error stream should be redirected
+ * to standard output stream.
+ *
+ * @return true
if standard error stream should be redirected
+ * to standard output stream
+ */
+ public boolean isRedirectErrorStream() {
+ return redirectErrorStream;
+ }
+
+ /**
+ * Returns the environment variables configured for the process.
+ *
+ * @return the environment variables configured for the process
+ */
+ @NonNull
+ public Map getEnvironmentVariables() {
+ return environmentVariables;
+ }
+}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java
--- a/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java
@@ -42,11 +42,14 @@
package org.netbeans.api.extexecution;
+import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.util.LinkedList;
@@ -358,7 +361,7 @@
final String[] lines = new String[] {"Process line \u1234", "Process line \u1235", "Process line \u1236"};
TestInputStream is = new TestInputStream(TestInputUtils.prepareInputStream(lines, "\n", charset, true));
- TestProcess process = new TestProcess(0, is);
+ TestProcess process = new TestProcess(0, is, null);
is.setProcess(process);
TestCallable callable = new TestCallable();
@@ -536,6 +539,123 @@
assertFalse(val.get());
}
+ public void testWriterService() throws InterruptedException, ExecutionException {
+ TestProcess process = new TestProcess(0,
+ new ByteArrayInputStream("testing input".getBytes()),
+ new ByteArrayInputStream("testing error".getBytes()));
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ StringWriter in = new StringWriter();
+ StringWriter err = new StringWriter();
+ ExecutionService service = ExecutionService.newService(callable, in, err);
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+
+ process.destroy();
+ process.waitFor();
+ assertTrue(process.isFinished());
+
+ assertEquals(0, task.get().intValue());
+ assertEquals("testing input", in.toString());
+ assertEquals("testing error", err.toString());
+ }
+
+ public void testWriterServiceOnce() throws InterruptedException, ExecutionException {
+ TestProcess process = new TestProcess(0,
+ new ByteArrayInputStream(new byte[0]), null);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ ExecutionService service = ExecutionService.newService(callable,
+ (Writer) null, (Writer) null);
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+
+ process.destroy();
+ process.waitFor();
+ assertTrue(process.isFinished());
+
+ assertEquals(0, task.get().intValue());
+
+ try {
+ service.run();
+ fail("Multiple run should fail with ISE");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ public void testLineService() throws InterruptedException, ExecutionException {
+ final String[] linesIn = new String[] {"Process line 1", "Process line 2", "Process line 3"};
+ final String[] linesErr = new String[] {"Error line 1", "Error line 2", "Error line 3"};
+
+ TestInputStream in = new TestInputStream(
+ TestInputUtils.prepareInputStream(linesIn, "\n", Charset.defaultCharset(), true));
+ TestInputStream err = new TestInputStream(
+ TestInputUtils.prepareInputStream(linesErr, "\n", Charset.defaultCharset(), true));
+ TestProcess process = new TestProcess(0, in, err);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ TestLineProcessor inProcessor = new TestLineProcessor(false);
+ TestLineProcessor errProcessor = new TestLineProcessor(false);
+ ExecutionService service = ExecutionService.newService(callable,
+ inProcessor, errProcessor, null);
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+
+ process.destroy();
+ process.waitFor();
+ assertTrue(process.isFinished());
+
+ assertEquals(0, task.get().intValue());
+
+ List processed = inProcessor.getLinesProcessed();
+ assertEquals(linesIn.length, processed.size());
+ for (int i = 0; i < linesIn.length; i++) {
+ assertEquals(linesIn[i], processed.get(i));
+ }
+ processed = errProcessor.getLinesProcessed();
+ assertEquals(linesErr.length, processed.size());
+ for (int i = 0; i < linesErr.length; i++) {
+ assertEquals(linesErr[i], processed.get(i));
+ }
+ }
+
+ public void testLineServiceOnce() throws InterruptedException, ExecutionException {
+ TestProcess process = new TestProcess(0,
+ new ByteArrayInputStream(new byte[0]), null);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ ExecutionService service = ExecutionService.newService(callable,
+ null, null, null);
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+
+ process.destroy();
+ process.waitFor();
+ assertTrue(process.isFinished());
+
+ assertEquals(0, task.get().intValue());
+
+ try {
+ service.run();
+ fail("Multiple run should fail with ISE");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
private static InputOutputManager.InputOutputData getInputOutput(String name,
boolean actions, String optionsPath) {
@@ -579,6 +699,8 @@
private final InputStream is;
+ private final InputStream err;
+
private boolean finished;
private boolean started;
@@ -586,12 +708,13 @@
public TestProcess(int returnValue) {
this(returnValue, TestInputUtils.prepareInputStream(
new String[] {"Process line 1", "Process line 2", "Process line 3"}, "\n",
- Charset.defaultCharset(), true));
+ Charset.defaultCharset(), true), null);
}
- public TestProcess(int returnValue, InputStream is) {
+ public TestProcess(int returnValue, InputStream is, InputStream err) {
this.returnValue = returnValue;
this.is = is;
+ this.err = err;
}
public void start() {
@@ -637,6 +760,9 @@
@Override
public InputStream getErrorStream() {
+ if (err != null) {
+ return err;
+ }
return new InputStream() {
@Override
public int read() throws IOException {
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java
--- a/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java
@@ -48,8 +48,13 @@
import java.util.List;
import java.util.Map;
import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.extexecution.EnvironmentFactory;
+import org.netbeans.spi.extexecution.EnvironmentImplementation;
import org.netbeans.spi.extexecution.ProcessBuilderFactory;
import org.netbeans.spi.extexecution.ProcessBuilderImplementation;
+import org.netbeans.spi.extexecution.ProcessBuilderImplementation2;
+import org.netbeans.spi.extexecution.ProcessParameters;
+import org.openide.util.Lookup;
/**
*
@@ -166,7 +171,7 @@
assertEquals("test2", testBuilder.getPaths().get(0));
}
- public void testEnvironment() throws IOException {
+ public void testEnvironmentVariables() throws IOException {
TestProcessBuilder testBuilder = new TestProcessBuilder();
ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
builder.setExecutable("ls");
@@ -201,6 +206,136 @@
assertEquals("value2", testBuilder.getEnvironment().get("key2"));
}
+ public void testEnvironment() throws IOException {
+ TestProcessBuilder2 testBuilder = new TestProcessBuilder2();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.getEnvironment().setVariable("key1", "value1");
+ builder.getEnvironment().setVariable("key2", "value2");
+
+ assertEquals("value1", testBuilder.getEnvironment().getVariable("key1"));
+ assertEquals("value2", testBuilder.getEnvironment().getVariable("key2"));
+
+ builder.call();
+ assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertEquals("value1", testBuilder.getParameters().getEnvironmentVariables().get("key1"));
+ assertEquals("value2", testBuilder.getParameters().getEnvironmentVariables().get("key2"));
+
+ builder.getEnvironment().prependPath("PATH", "/test1");
+ builder.getEnvironment().prependPath("PATH", "/test2");
+
+ builder.call();
+ assertEquals(3, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertEquals("/test2:/test1",
+ testBuilder.getParameters().getEnvironmentVariables().get("PATH"));
+
+ builder.getEnvironment().appendPath("PATH", "/test3");
+
+ builder.call();
+ assertEquals(3, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertEquals("/test2:/test1:/test3",
+ testBuilder.getParameters().getEnvironmentVariables().get("PATH"));
+
+ builder.getEnvironment().removeVariable("PATH");
+ assertNull(builder.getEnvironment().getVariable("PATH"));
+
+ builder.call();
+ assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertNull(testBuilder.getParameters().getEnvironmentVariables().get("PATH"));
+ }
+
+ public void testFallbackEnvironment() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.getEnvironment().setVariable("key1", "value1");
+ builder.getEnvironment().setVariable("key2", "value2");
+
+ builder.call();
+ assertEquals(2, testBuilder.getEnvironment().size());
+ assertEquals("value1", testBuilder.getEnvironment().get("key1"));
+ assertEquals("value2", testBuilder.getEnvironment().get("key2"));
+
+ builder.getEnvironment().prependPath("PATH", "/test1");
+ builder.getEnvironment().prependPath("Path", "/test2");
+
+ builder.call();
+ assertEquals(2, testBuilder.getPaths().size());
+ assertEquals("/test2", testBuilder.getPaths().get(0));
+ assertEquals("/test1", testBuilder.getPaths().get(1));
+
+ try {
+ builder.getEnvironment().prependPath("LD_LIBRARY_PATH", "/test");
+ fail("Prepend of unknown path does not throw exception");
+ } catch (UnsupportedOperationException ex) {
+ // expected
+ }
+
+ try {
+ builder.getEnvironment().appendPath("PATH", "/test");
+ fail("Append of any path does not throw exception");
+ } catch (UnsupportedOperationException ex) {
+ // expected
+ }
+
+ try {
+ builder.getEnvironment().getVariable("PATH");
+ fail("getVariable() does not throw exception");
+ } catch (UnsupportedOperationException ex) {
+ // expected
+ }
+
+ try {
+ builder.getEnvironment().removeVariable("PATH");
+ fail("removeVariable() does not throw exception");
+ } catch (UnsupportedOperationException ex) {
+ // expected
+ }
+
+ try {
+ builder.getEnvironment().values();
+ fail("values() does not throw exception");
+ } catch (UnsupportedOperationException ex) {
+ // expected
+ }
+ }
+
+ public void testFallbackEnvironmentCombined() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.getEnvironment().setVariable("key1", "value1");
+ builder.getEnvironment().setVariable("key2", "value2");
+
+ builder.call();
+ assertEquals(2, testBuilder.getEnvironment().size());
+ assertEquals("value1", testBuilder.getEnvironment().get("key1"));
+ assertEquals("value2", testBuilder.getEnvironment().get("key2"));
+
+ builder.setEnvironmentVariables(Collections.singletonMap("key3", "value3"));
+
+ builder.call();
+ assertEquals(1, testBuilder.getEnvironment().size());
+ assertEquals("value3", testBuilder.getEnvironment().get("key3"));
+
+ builder.getEnvironment().prependPath("PATH", "/test1");
+ builder.getEnvironment().prependPath("Path", "/test2");
+
+ builder.call();
+ assertEquals(2, testBuilder.getPaths().size());
+ assertEquals("/test2", testBuilder.getPaths().get(0));
+ assertEquals("/test1", testBuilder.getPaths().get(1));
+
+ builder.setPaths(Collections.singletonList("/test3"));
+
+ builder.call();
+ assertEquals(1, testBuilder.getPaths().size());
+ assertEquals("/test3", testBuilder.getPaths().get(0));
+ }
+
public void testRedirectErrorStream() throws IOException {
TestProcessBuilder testBuilder = new TestProcessBuilder();
ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
@@ -216,7 +351,7 @@
assertTrue(testBuilder.isRedirectErrorStream());
}
- private class TestProcessBuilder implements ProcessBuilderImplementation {
+ private static class TestProcessBuilder implements ProcessBuilderImplementation {
private String executable;
@@ -267,6 +402,80 @@
public String getWorkingDirectory() {
return workingDirectory;
}
+ }
+ private static class TestProcessBuilder2 implements ProcessBuilderImplementation2 {
+
+ private final Environment environment = EnvironmentFactory.createEnvironment(new TestEnvironment());
+
+ private ProcessParameters parameters;
+
+ @Override
+ public Environment getEnvironment() {
+ return environment;
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return Lookup.EMPTY;
+ }
+
+ @Override
+ public Process createProcess(ProcessParameters parameters) throws IOException {
+ this.parameters = parameters;
+
+ return null;
+ }
+
+ public ProcessParameters getParameters() {
+ return parameters;
+ }
+ }
+
+ private static class TestEnvironment implements EnvironmentImplementation {
+
+ private final Map values = new HashMap();
+
+ @Override
+ public String getVariable(String name) {
+ return values.get(name);
+ }
+
+ @Override
+ public void appendPath(String name, String value) {
+ String orig = values.get(name);
+ if (orig == null || orig.isEmpty()) {
+ values.put(name, value);
+ } else {
+ // intentionally hardcoded for tests
+ values.put(name, orig + ":" + value);
+ }
+ }
+
+ @Override
+ public void prependPath(String name, String value) {
+ String orig = values.get(name);
+ if (orig == null || orig.isEmpty()) {
+ values.put(name, value);
+ } else {
+ // intentionally hardcoded for tests
+ values.put(name, value + ":" + orig);
+ }
+ }
+
+ @Override
+ public void setVariable(String name, String value) {
+ values.put(name, value);
+ }
+
+ @Override
+ public void removeVariable(String name) {
+ values.remove(name);
+ }
+
+ @Override
+ public Map values() {
+ return new HashMap(values);
+ }
}
}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java
--- a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java
@@ -47,6 +47,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.netbeans.junit.NbTestCase;
@@ -85,4 +86,22 @@
assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed()));
}
+
+ public void testReadStringReader() throws IOException {
+ Reader reader = new StringReader(new String(TEST_CHARS));
+ InputReader inputReader = InputReaders.forReader(reader);
+ TestInputProcessor processor = new TestInputProcessor(false);
+
+ int read = 0;
+ int retries = 0;
+ while (read < TEST_CHARS.length && retries < MAX_RETRIES) {
+ read += inputReader.readInput(processor);
+ retries++;
+ }
+
+ assertEquals(read, TEST_CHARS.length);
+ assertEquals(0, processor.getResetCount());
+
+ assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed()));
+ }
}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java
@@ -0,0 +1,91 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import java.nio.charset.Charset;
+import org.netbeans.junit.NbTestCase;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessCharsetTest extends NbTestCase {
+
+ public ProcessCharsetTest(String name) {
+ super(name);
+ }
+
+ public void testProcessCharset() {
+ assertFalse(ProcessCharset.isSupported(new TestProcess(Lookup.EMPTY)));
+
+ assertNull(ProcessCharset.getOutputCharset(new TestProcess(Lookup.EMPTY)));
+ assertNull(ProcessCharset.getInputCharset(new TestProcess(Lookup.EMPTY)));
+ assertNull(ProcessCharset.getErrorCharset(new TestProcess(Lookup.EMPTY)));
+
+ TestProcessCharset charset = new TestProcessCharset();
+ TestProcess process = new TestProcess(Lookups.fixed(charset));
+ assertTrue(ProcessCharset.isSupported(process));
+ assertEquals(charset.getOutputCharset(), ProcessCharset.getOutputCharset(process));
+ assertEquals(charset.getInputCharset(), ProcessCharset.getInputCharset(process));
+ assertEquals(charset.getErrorCharset(), ProcessCharset.getErrorCharset(process));
+ }
+
+ private static class TestProcessCharset extends ProcessCharset {
+
+ @Override
+ protected Charset getInputCharset() {
+ return Charset.forName("US-ASCII");
+ }
+
+ @Override
+ protected Charset getOutputCharset() {
+ return Charset.forName("ISO-8859-1");
+ }
+
+ @Override
+ protected Charset getErrorCharset() {
+ return Charset.forName("UTF-8");
+ }
+ }
+}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java
@@ -0,0 +1,76 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import org.netbeans.junit.NbTestCase;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessIdTest extends NbTestCase {
+
+ public ProcessIdTest(String name) {
+ super(name);
+ }
+
+ public void testProcessId() {
+ assertFalse(ProcessId.isSupported(new TestProcess(Lookup.EMPTY)));
+
+ assertNull(ProcessId.getId(new TestProcess(Lookup.EMPTY)));
+
+ TestProcessId id = new TestProcessId();
+ TestProcess process = new TestProcess(Lookups.fixed(id));
+ assertTrue(ProcessId.isSupported(process));
+ assertEquals(id.getId(), ProcessId.getId(process));
+ }
+
+ private static class TestProcessId extends ProcessId {
+
+ @Override
+ protected Integer getId() {
+ return 1;
+ }
+ }
+}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java
@@ -0,0 +1,116 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import java.io.IOException;
+import org.netbeans.junit.NbTestCase;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessSignalTest extends NbTestCase {
+
+ public ProcessSignalTest(String name) {
+ super(name);
+ }
+
+ public void testProcessSignal() throws IOException {
+ assertFalse(ProcessSignal.isSupported(new TestProcess(Lookup.EMPTY)));
+
+ TestProcessSignal signal = new TestProcessSignal();
+ TestProcess process = new TestProcess(Lookups.fixed(signal));
+ assertTrue(ProcessSignal.isSupported(process));
+
+ ProcessSignal.signal(process, ProcessSignal.Signal.SIGALRM);
+ ProcessSignal.signalGroup(process, ProcessSignal.Signal.SIGCANCEL);
+
+ assertEquals(ProcessSignal.Signal.SIGALRM, signal.getSignal());
+ assertEquals(ProcessSignal.Signal.SIGCANCEL, signal.getGroupSignal());
+
+ try {
+ ProcessSignal.signal(process, ProcessSignal.Signal.NULL);
+ fail("No exception propagated");
+ } catch (IOException ex) {
+ // expected
+ }
+ try {
+ ProcessSignal.signalGroup(process, ProcessSignal.Signal.NULL);
+ fail("No exception propagated");
+ } catch (IOException ex) {
+ // expected
+ }
+ }
+
+ private static class TestProcessSignal extends ProcessSignal {
+
+ private Signal signal;
+
+ private Signal groupSignal;
+
+ @Override
+ protected void signal(Signal signal) throws IOException {
+ if (this.signal != null) {
+ throw new IOException("Test");
+ }
+ this.signal = signal;
+ }
+
+ @Override
+ protected void signalGroup(Signal signal) throws IOException {
+ if (this.groupSignal != null) {
+ throw new IOException("Test");
+ }
+ this.groupSignal = signal;
+ }
+
+ public Signal getSignal() {
+ return signal;
+ }
+
+ public Signal getGroupSignal() {
+ return groupSignal;
+ }
+ }
+}
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java
new file mode 100644
--- /dev/null
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java
@@ -0,0 +1,94 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.api.extexecution.process;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class TestProcess extends Process implements Lookup.Provider {
+
+ private final Lookup lookup;
+
+ public TestProcess(Lookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public Lookup getLookup() {
+ return lookup;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int exitValue() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void destroy() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+}
diff --git a/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java b/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java
@@ -0,0 +1,79 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2013 Sun Microsystems, Inc.
+ */
+package org.netbeans.spi.extexecution;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.extexecution.ProcessParametersAccessor;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessParametersTest extends NbTestCase {
+
+ public ProcessParametersTest(String name) {
+ super(name);
+ }
+
+ public void testParameters() {
+ Map variables = new HashMap();
+ variables.put("key1", "value1");
+ variables.put("key2", "value2");
+
+ ProcessParameters params = ProcessParametersAccessor.getDefault().createProcessParameters(
+ "ls", "/home", Collections.singletonList("argument"), true, variables);
+
+ assertEquals("ls", params.getExecutable());
+ assertEquals("/home", params.getWorkingDirectory());
+ assertTrue(params.isRedirectErrorStream());
+
+ assertEquals(1, params.getArguments().size());
+ assertEquals("argument", params.getArguments().get(0));
+
+ assertEquals(2, params.getEnvironmentVariables().size());
+ assertEquals("value1", params.getEnvironmentVariables().get("key1"));
+ assertEquals("value2", params.getEnvironmentVariables().get("key2"));
+ }
+}