+ This API origins in External Execution API and contains set of basic
+ features with minimal dependencies. For the better integration with
+ the IDE check also original External Execution API.
+
+
+ The External Execution Base module provides the
+
+ that contains support for execution of external processes. There is also
+ abstraction of process builder and support class for extended process handling.
+
+
+ Another exported API
+
+ define interfaces for input processing (character or line based) and provides
+ common implementations of these with factory methods.
+
+
+ The SPI
+
+ allows different implementations of process builder and defined interface
+ for extended process handling support.
+
+
+
+
+
+
+
+
+ Most of the API functionality is covered by unit tests. Same applies to
+ future enhancements.
+
+
+
+
+
+
+
+
+ Written and functional. Compatible changes can occur in future.
+
+
+
+
+
+
+
+
+
+ Client needs to execute an external process and handle process streams.
+
+
+ In order to achieve this client creates the
+ BaseExecutionDescriptor.
+ Via this object client configures all the client integration parameters of
+ the execution. As a next step client creates the
+ BaseExecutionService
+ itself and calls run to execute the job. Run can be called multiple times.
+ The output and input streams are handled by the service. Additional
+ processing can be configured in descriptor through interfaces described
+ in following usecases.
+
+
+ The creation of the external process is supported by
+ ProcessBuilder
+ to make things easier.
+
+
+
+
+ Client needs to process character data coming from stream, file or other
+ source.
+
+
+ To abstract the source of the data client must implement
+ InputReader.
+ To abstract the data processing client must implement
+ InputProcessor or
+ LineProcessor.
+ For all three interfaces there are prepared common implementations (and bridge
+ from character based to line based processing) at these three factory classes:
+
+ Once the data source and processing objects are prepared client creates
+ InputReaderTask.
+ Factory methods of the InputReaderTask
+ can create either common task exiting on interruption or cancellation
+ or draining task which is trying to drain out all available data before exiting.
+
+
+
+
+ Third party wants to implement custom process builder to provide
+ additional functionality, such as remote execution.
+
+ Provides common APIs to execute external process and to handle its
+ streams and process the output. Input/line processing can be used
+ as separate part.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The module languages.execution should be removed as it provides not well
+ stabilized subset of the same functionality.
+
+
+
+
+
+
+
+
+ Yes.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Yes. No settings stored.
+
+
+
+
+
+
+
+
+ 1.5
+
+
+
+
+
+
+
+
+ JRE is enough.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ None.
+
+
+
+
+
+
+
+
+ No known platform dependencies.
+
+
+
+
+
+
+
+
+ Nothing.
+
+
+
+
+
+
+
+
+ Just the single JAR file.
+
+
+
+
+
+
+
+
+ Yes.
+
+
+
+
+
+
+
+
+ Only API packages are exported.
+
+
+
+
+
+
+
+
+ Anywhere.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Yes. The API provides support to do so. The result code, input and
+ output stream content does not define API as this is forwarded to the client
+ of this module.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Each class and factory method defines the thread safety of the class.
+ If this is missing by accident method can be called from any thread.
+
+
+
+
+
+
+
+
+ None.
+
+
+
+
+
+
+
+
+ None.
+
+
+
+
+
+
+
+
+ None.
+
+
+
+
+
+
+
+
+ The class for extended process handling
+ Processes
+ is trying to lookup an implementation
+ ProcessesImplementation
+ in default lookup.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ On JVM shutdown module tries to terminate any running process executed
+ through the API.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Any spawned process needs 4 threads (the worst case). One as the process
+ handler, one for the standard input, one for the standard output and one for
+ the standard error output. The minimal number of threads to handle a process
+ is 2 (process handler and standard output handler - standard error output
+ is redirected to the output, no thread for the standard input).
+
+
+ Typically the client should not run more than 10 external processes
+ concurrently.
+
+
+
+
+
+
+
+
+ The small amount of the memory is consumed by caching data structures like
+ available output tabs and currently executed processes.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Executing external processes. Always scheduled to dedicated thread.
+
+
+
+
+
+
+
+
+ Number of threads depending on number of spawned processes (n) increases
+ lineary (4n the worst case, 2n the best case).
+
+
+
+
+
+
+
+
+ No enforcement. SPI code may be used to terminate whole process tree.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ Yes.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
+
+
+
+
+ No.
+
+
+
+
diff --git a/extexecution.base/build.xml b/extexecution.base/build.xml
new file mode 100644
--- /dev/null
+++ b/extexecution.base/build.xml
@@ -0,0 +1,5 @@
+
+
+ Builds, tests, and runs the project org.netbeans.modules.extexecution.base
+
+
diff --git a/extexecution.base/manifest.mf b/extexecution.base/manifest.mf
new file mode 100644
--- /dev/null
+++ b/extexecution.base/manifest.mf
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: false
+OpenIDE-Module: org.netbeans.modules.extexecution.base/2
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/extexecution/base/resources/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+
diff --git a/extexecution.base/nbproject/org-netbeans-modules-extexecution.sig b/extexecution.base/nbproject/org-netbeans-modules-extexecution.sig
new file mode 100644
--- /dev/null
+++ b/extexecution.base/nbproject/org-netbeans-modules-extexecution.sig
@@ -0,0 +1,487 @@
+#Signature file v4.1
+#Version 1.41.1
+
+CLSS public abstract interface java.io.Closeable
+intf java.lang.AutoCloseable
+meth public abstract void close() throws java.io.IOException
+
+CLSS public abstract interface java.io.Serializable
+
+CLSS public abstract interface java.lang.AutoCloseable
+meth public abstract void close() throws java.lang.Exception
+
+CLSS public abstract interface java.lang.Comparable<%0 extends java.lang.Object>
+meth public abstract int compareTo({java.lang.Comparable%0})
+
+CLSS public abstract java.lang.Enum<%0 extends java.lang.Enum<{java.lang.Enum%0}>>
+cons protected init(java.lang.String,int)
+intf java.io.Serializable
+intf java.lang.Comparable<{java.lang.Enum%0}>
+meth protected final java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected final void finalize()
+meth public final boolean equals(java.lang.Object)
+meth public final int compareTo({java.lang.Enum%0})
+meth public final int hashCode()
+meth public final int ordinal()
+meth public final java.lang.Class<{java.lang.Enum%0}> getDeclaringClass()
+meth public final java.lang.String name()
+meth public java.lang.String toString()
+meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Class<{%%0}>,java.lang.String)
+supr java.lang.Object
+hfds name,ordinal
+
+CLSS public java.lang.Object
+cons public init()
+meth protected java.lang.Object clone() throws java.lang.CloneNotSupportedException
+meth protected void finalize() throws java.lang.Throwable
+meth public boolean equals(java.lang.Object)
+meth public final java.lang.Class> getClass()
+meth public final void notify()
+meth public final void notifyAll()
+meth public final void wait() throws java.lang.InterruptedException
+meth public final void wait(long) throws java.lang.InterruptedException
+meth public final void wait(long,int) throws java.lang.InterruptedException
+meth public int hashCode()
+meth public java.lang.String toString()
+
+CLSS public abstract interface java.lang.Runnable
+meth public abstract void run()
+
+CLSS public abstract interface java.lang.annotation.Annotation
+meth public abstract boolean equals(java.lang.Object)
+meth public abstract int hashCode()
+meth public abstract java.lang.Class extends java.lang.annotation.Annotation> annotationType()
+meth public abstract java.lang.String toString()
+
+CLSS public abstract interface !annotation java.lang.annotation.Documented
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+
+CLSS public abstract interface !annotation java.lang.annotation.Retention
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.RetentionPolicy value()
+
+CLSS public abstract interface !annotation java.lang.annotation.Target
+ anno 0 java.lang.annotation.Documented()
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=RUNTIME)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[ANNOTATION_TYPE])
+intf java.lang.annotation.Annotation
+meth public abstract java.lang.annotation.ElementType[] value()
+
+CLSS public abstract interface java.util.concurrent.Callable<%0 extends java.lang.Object>
+meth public abstract {java.util.concurrent.Callable%0} call() throws java.lang.Exception
+
+CLSS public final org.netbeans.api.extexecution.ExecutionDescriptor
+cons public init()
+innr public abstract interface static InputProcessorFactory
+innr public abstract interface static LineConvertorFactory
+innr public abstract interface static RerunCondition
+meth public org.netbeans.api.extexecution.ExecutionDescriptor charset(java.nio.charset.Charset)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor controllable(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor errConvertorFactory(org.netbeans.api.extexecution.ExecutionDescriptor$LineConvertorFactory)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor errLineBased(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor errProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor$InputProcessorFactory)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor frontWindow(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor frontWindowOnError(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor inputOutput(org.openide.windows.InputOutput)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor inputVisible(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor noReset(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor optionsPath(java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor outConvertorFactory(org.netbeans.api.extexecution.ExecutionDescriptor$LineConvertorFactory)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor outLineBased(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor outProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor$InputProcessorFactory)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor postExecution(java.lang.Runnable)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor preExecution(java.lang.Runnable)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor rerunCondition(org.netbeans.api.extexecution.ExecutionDescriptor$RerunCondition)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor showProgress(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExecutionDescriptor showSuspended(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds charset,controllable,errConvertorFactory,errLineBased,errProcessorFactory,front,frontWindowOnError,input,inputOutput,noReset,optionsPath,outConvertorFactory,outLineBased,outProcessorFactory,postExecution,preExecution,progress,rerunCondition,suspend
+hcls DescriptorData
+
+CLSS public abstract interface static org.netbeans.api.extexecution.ExecutionDescriptor$InputProcessorFactory
+ outer org.netbeans.api.extexecution.ExecutionDescriptor
+meth public abstract org.netbeans.api.extexecution.input.InputProcessor newInputProcessor(org.netbeans.api.extexecution.input.InputProcessor)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface static org.netbeans.api.extexecution.ExecutionDescriptor$LineConvertorFactory
+ outer org.netbeans.api.extexecution.ExecutionDescriptor
+meth public abstract org.netbeans.api.extexecution.print.LineConvertor newLineConvertor()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface static org.netbeans.api.extexecution.ExecutionDescriptor$RerunCondition
+ outer org.netbeans.api.extexecution.ExecutionDescriptor
+meth public abstract boolean isRerunPossible()
+meth public abstract void addChangeListener(javax.swing.event.ChangeListener)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public abstract void removeChangeListener(javax.swing.event.ChangeListener)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public final org.netbeans.api.extexecution.ExecutionService
+meth public java.util.concurrent.Future run()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.ExecutionService newService(java.util.concurrent.Callable,org.netbeans.api.extexecution.ExecutionDescriptor,java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+ anno 3 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds EXECUTOR_SERVICE,EXECUTOR_SHUTDOWN_SLICE,LOGGER,RUNNING_PROCESSES,descriptor,originalDisplayName,processCreator
+hcls ProgressAction,ProgressCancellable,WrappedException
+
+CLSS public final org.netbeans.api.extexecution.ExternalProcessBuilder
+cons public init(java.lang.String)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+intf java.util.concurrent.Callable
+meth public java.lang.Process call() throws java.io.IOException
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExternalProcessBuilder addArgument(java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExternalProcessBuilder addEnvironmentVariable(java.lang.String,java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExternalProcessBuilder prependPath(java.io.File)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExternalProcessBuilder redirectErrorStream(boolean)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.netbeans.api.extexecution.ExternalProcessBuilder workingDirectory(java.io.File)
+ anno 0 org.netbeans.api.annotations.common.CheckReturnValue()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds ESCAPED_PATTERN,LOGGER,PROXY_AUTHENTICATION_PASSWORD,PROXY_AUTHENTICATION_USERNAME,USE_PROXY_AUTHENTICATION,arguments,envVariables,executable,paths,redirectErrorStream,workingDirectory
+hcls BuilderData
+
+CLSS public final org.netbeans.api.extexecution.ExternalProcessSupport
+meth public static void destroy(java.lang.Process,java.util.Map)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+
+CLSS public final org.netbeans.api.extexecution.ProcessBuilder
+intf java.util.concurrent.Callable
+meth public java.lang.Process call() throws java.io.IOException
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public java.lang.String getDescription()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.ProcessBuilder getLocal()
+meth public void setArguments(java.util.List)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public void setEnvironmentVariables(java.util.Map)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public void setExecutable(java.lang.String)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public void setPaths(java.util.List)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public void setRedirectErrorStream(boolean)
+meth public void setWorkingDirectory(java.lang.String)
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+supr java.lang.Object
+hfds arguments,description,envVariables,executable,implementation,paths,redirectErrorStream,workingDirectory
+hcls LocalProcessFactory
+
+CLSS public abstract interface org.netbeans.api.extexecution.input.InputProcessor
+intf java.io.Closeable
+meth public abstract void close() throws java.io.IOException
+meth public abstract void processInput(char[]) throws java.io.IOException
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public abstract void reset() throws java.io.IOException
+
+CLSS public final org.netbeans.api.extexecution.input.InputProcessors
+meth public !varargs static org.netbeans.api.extexecution.input.InputProcessor proxy(org.netbeans.api.extexecution.input.InputProcessor[])
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputProcessor ansiStripping(org.netbeans.api.extexecution.input.InputProcessor)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputProcessor bridge(org.netbeans.api.extexecution.input.LineProcessor)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputProcessor copying(java.io.Writer)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputProcessor printing(org.openide.windows.OutputWriter,boolean)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputProcessor printing(org.openide.windows.OutputWriter,org.netbeans.api.extexecution.print.LineConvertor,boolean)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+supr java.lang.Object
+hfds LOGGER
+hcls AnsiStrippingInputProcessor,Bridge,CopyingInputProcessor,PrintingInputProcessor,ProxyInputProcessor
+
+CLSS public abstract interface org.netbeans.api.extexecution.input.InputReader
+intf java.io.Closeable
+meth public abstract int readInput(org.netbeans.api.extexecution.input.InputProcessor) throws java.io.IOException
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+meth public abstract void close() throws java.io.IOException
+
+CLSS public final org.netbeans.api.extexecution.input.InputReaderTask
+intf java.lang.Runnable
+intf org.openide.util.Cancellable
+meth public boolean cancel()
+meth public static org.netbeans.api.extexecution.input.InputReaderTask newDrainingTask(org.netbeans.api.extexecution.input.InputReader,org.netbeans.api.extexecution.input.InputProcessor)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+meth public static org.netbeans.api.extexecution.input.InputReaderTask newTask(org.netbeans.api.extexecution.input.InputReader,org.netbeans.api.extexecution.input.InputProcessor)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+meth public void run()
+supr java.lang.Object
+hfds DELAY_INCREMENT,LOGGER,MAX_DELAY,MIN_DELAY,cancelled,draining,inputProcessor,inputReader,running
+
+CLSS public final org.netbeans.api.extexecution.input.InputReaders
+innr public final static FileInput
+meth public static org.netbeans.api.extexecution.input.InputReader forFile(java.io.File,java.nio.charset.Charset)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputReader forFileInputProvider(org.netbeans.api.extexecution.input.InputReaders$FileInput$Provider)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputReader forReader(java.io.Reader)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.InputReader forStream(java.io.InputStream,java.nio.charset.Charset)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+
+CLSS public final static org.netbeans.api.extexecution.input.InputReaders$FileInput
+ outer org.netbeans.api.extexecution.input.InputReaders
+cons public init(java.io.File,java.nio.charset.Charset)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+innr public abstract interface static Provider
+meth public java.io.File getFile()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public java.nio.charset.Charset getCharset()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds charset,file
+
+CLSS public abstract interface static org.netbeans.api.extexecution.input.InputReaders$FileInput$Provider
+ outer org.netbeans.api.extexecution.input.InputReaders$FileInput
+meth public abstract org.netbeans.api.extexecution.input.InputReaders$FileInput getFileInput()
+ anno 0 org.netbeans.api.annotations.common.CheckForNull()
+
+CLSS public abstract interface org.netbeans.api.extexecution.input.LineProcessor
+intf java.io.Closeable
+meth public abstract void close()
+meth public abstract void processLine(java.lang.String)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public abstract void reset()
+
+CLSS public final org.netbeans.api.extexecution.input.LineProcessors
+meth public !varargs static org.netbeans.api.extexecution.input.LineProcessor proxy(org.netbeans.api.extexecution.input.LineProcessor[])
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.LineProcessor patternWaiting(java.util.regex.Pattern,java.util.concurrent.CountDownLatch)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.LineProcessor printing(org.openide.windows.OutputWriter,boolean)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.input.LineProcessor printing(org.openide.windows.OutputWriter,org.netbeans.api.extexecution.print.LineConvertor,boolean)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+supr java.lang.Object
+hfds LOGGER
+hcls PrintingLineProcessor,ProxyLineProcessor,WaitingLineProcessor
+
+CLSS abstract interface org.netbeans.api.extexecution.input.package-info
+
+CLSS abstract interface org.netbeans.api.extexecution.package-info
+
+CLSS public final org.netbeans.api.extexecution.print.ConvertedLine
+meth public java.lang.String getText()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public org.openide.windows.OutputListener getListener()
+ anno 0 org.netbeans.api.annotations.common.CheckForNull()
+meth public static org.netbeans.api.extexecution.print.ConvertedLine forText(java.lang.String,org.openide.windows.OutputListener)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+supr java.lang.Object
+hfds listener,text
+
+CLSS public abstract interface org.netbeans.api.extexecution.print.LineConvertor
+meth public abstract java.util.List convert(java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.CheckForNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public final org.netbeans.api.extexecution.print.LineConvertors
+innr public abstract interface static FileLocator
+meth public !varargs static org.netbeans.api.extexecution.print.LineConvertor proxy(org.netbeans.api.extexecution.print.LineConvertor[])
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+meth public static org.netbeans.api.extexecution.print.LineConvertor filePattern(org.netbeans.api.extexecution.print.LineConvertors$FileLocator,java.util.regex.Pattern,java.util.regex.Pattern,int,int)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NullAllowed()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+ anno 3 org.netbeans.api.annotations.common.NullAllowed()
+meth public static org.netbeans.api.extexecution.print.LineConvertor httpUrl()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds DEFAULT_FILE_HANDLER,DEFAULT_HTTP_HANDLER,LOGGER
+hcls FilePatternConvertor,HttpUrlConvertor,ProxyLineConvertor
+
+CLSS public abstract interface static org.netbeans.api.extexecution.print.LineConvertors$FileLocator
+ outer org.netbeans.api.extexecution.print.LineConvertors
+meth public abstract org.openide.filesystems.FileObject find(java.lang.String)
+ anno 0 org.netbeans.api.annotations.common.CheckForNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS abstract interface org.netbeans.api.extexecution.print.package-info
+
+CLSS public final org.netbeans.api.extexecution.startup.StartupExtender
+innr public final static !enum StartMode
+meth public java.lang.String getDescription()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public java.util.List getArguments()
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+meth public static java.util.List getExtenders(org.openide.util.Lookup,org.netbeans.api.extexecution.startup.StartupExtender$StartMode)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+supr java.lang.Object
+hfds LOG,arguments,description
+
+CLSS public final static !enum org.netbeans.api.extexecution.startup.StartupExtender$StartMode
+ outer org.netbeans.api.extexecution.startup.StartupExtender
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode DEBUG
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode NORMAL
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode PROFILE
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode TEST_DEBUG
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode TEST_NORMAL
+fld public final static org.netbeans.api.extexecution.startup.StartupExtender$StartMode TEST_PROFILE
+meth public java.lang.String toString()
+meth public static org.netbeans.api.extexecution.startup.StartupExtender$StartMode valueOf(java.lang.String)
+meth public static org.netbeans.api.extexecution.startup.StartupExtender$StartMode[] values()
+supr java.lang.Enum
+hfds mode
+
+CLSS abstract interface org.netbeans.api.extexecution.startup.package-info
+
+CLSS public org.netbeans.spi.extexecution.ProcessBuilderFactory
+meth public static org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder(org.netbeans.spi.extexecution.ProcessBuilderImplementation,java.lang.String)
+supr java.lang.Object
+
+CLSS public abstract interface org.netbeans.spi.extexecution.ProcessBuilderImplementation
+meth public abstract java.lang.Process createProcess(java.lang.String,java.lang.String,java.util.List,java.util.List,java.util.Map,boolean) throws java.io.IOException
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NullAllowed()
+ anno 3 org.netbeans.api.annotations.common.NonNull()
+ anno 4 org.netbeans.api.annotations.common.NonNull()
+ anno 5 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface org.netbeans.spi.extexecution.destroy.ProcessDestroyPerformer
+meth public abstract void destroy(java.lang.Process,java.util.Map)
+
+CLSS abstract interface org.netbeans.spi.extexecution.destroy.package-info
+
+CLSS public abstract interface org.netbeans.spi.extexecution.open.FileOpenHandler
+meth public abstract void open(org.openide.filesystems.FileObject,int)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface org.netbeans.spi.extexecution.open.HttpOpenHandler
+meth public abstract void open(java.net.URL)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface org.netbeans.spi.extexecution.open.OptionOpenHandler
+meth public abstract void open(java.lang.String)
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+
+CLSS abstract interface org.netbeans.spi.extexecution.open.package-info
+
+CLSS abstract interface org.netbeans.spi.extexecution.package-info
+
+CLSS public abstract interface org.netbeans.spi.extexecution.startup.StartupExtenderImplementation
+innr public abstract interface static !annotation Registration
+meth public abstract java.util.List getArguments(org.openide.util.Lookup,org.netbeans.api.extexecution.startup.StartupExtender$StartMode)
+ anno 0 org.netbeans.api.annotations.common.NonNull()
+ anno 1 org.netbeans.api.annotations.common.NonNull()
+ anno 2 org.netbeans.api.annotations.common.NonNull()
+
+CLSS public abstract interface static !annotation org.netbeans.spi.extexecution.startup.StartupExtenderImplementation$Registration
+ outer org.netbeans.spi.extexecution.startup.StartupExtenderImplementation
+ anno 0 java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy value=SOURCE)
+ anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[TYPE, METHOD])
+intf java.lang.annotation.Annotation
+meth public abstract !hasdefault int position()
+meth public abstract java.lang.String displayName()
+meth public abstract org.netbeans.api.extexecution.startup.StartupExtender$StartMode[] startMode()
+
+CLSS abstract interface org.netbeans.spi.extexecution.startup.package-info
+
+CLSS public abstract interface org.openide.util.Cancellable
+meth public abstract boolean cancel()
+
diff --git a/extexecution.base/nbproject/project.properties b/extexecution.base/nbproject/project.properties
new file mode 100644
--- /dev/null
+++ b/extexecution.base/nbproject/project.properties
@@ -0,0 +1,53 @@
+#
+# 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-2006 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.
+
+is.autoload=true
+javac.source=1.6
+
+javadoc.arch=${basedir}/arch.xml
+javadoc.apichanges=${basedir}/apichanges.xml
+nbm.module.author=Petr Hejl
+
+test.config.stableBTD.includes=**/*Test.class
+test.config.stableBTD.excludes=\
+ **/ExecutionServiceTest.class
diff --git a/extexecution.base/nbproject/project.xml b/extexecution.base/nbproject/project.xml
new file mode 100644
--- /dev/null
+++ b/extexecution.base/nbproject/project.xml
@@ -0,0 +1,70 @@
+
+
+ org.netbeans.modules.apisupport.project
+
+
+ org.netbeans.modules.extexecution.base
+
+
+ org.netbeans.api.annotations.common
+
+
+
+ 1
+ 1.0
+
+
+
+ org.openide.util.base
+
+
+
+ 9.1
+
+
+
+ org.openide.util.lookup
+
+
+
+ 8.0
+
+
+
+
+
+ unit
+
+ org.netbeans.modules.extexecution.base
+
+
+
+
+ org.netbeans.modules.nbjunit
+
+
+
+
+ org.netbeans.libs.junit4
+
+
+
+ org.openide.util.base
+
+
+
+
+ org.openide.util.lookup
+
+
+
+
+
+
+ org.netbeans.api.extexecution.base
+ org.netbeans.api.extexecution.base.input
+ org.netbeans.spi.extexecution.base
+
+
+
+
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionDescriptor.java b/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionDescriptor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionDescriptor.java
@@ -0,0 +1,283 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base;
+
+import java.io.Reader;
+import java.nio.charset.Charset;
+import org.netbeans.api.annotations.common.CheckReturnValue;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+
+/**
+ * Descriptor for the execution service. Describes the runtime attributes
+ * of the {@link BaseExecutionService}.
+ *
+ * Thread safety of this class depends on type of objects passed to its
+ * configuration methods. If these objects are immutable, resulting descriptor
+ * is immutable as well. It these objects are thread safe, resulting descriptor
+ * is thread safe as well.
+ *
+ * @author Petr Hejl
+ * @see BaseExecutionService
+ */
+public final class BaseExecutionDescriptor {
+
+ private final Charset charset;
+
+ private final Runnable preExecution;
+
+ private final ParametrizedRunnable postExecution;
+
+ private final InputProcessorFactory outProcessorFactory;
+
+ private final InputProcessorFactory errProcessorFactory;
+
+ private final ReaderFactory inReaderFactory;
+
+ /**
+ * Creates the new descriptor. All properties are initialized to
+ * null.
+ */
+ public BaseExecutionDescriptor() {
+ this(null, null, null, null, null, null);
+ }
+
+ private BaseExecutionDescriptor(Charset charset, Runnable preExecution,
+ ParametrizedRunnable postExecution,
+ InputProcessorFactory outProcessorFactory,
+ InputProcessorFactory errProcessorFactory,
+ ReaderFactory inReaderFactory) {
+
+ this.charset = charset;
+ this.preExecution = preExecution;
+ this.postExecution = postExecution;
+ this.outProcessorFactory = outProcessorFactory;
+ this.errProcessorFactory = errProcessorFactory;
+ this.inReaderFactory = inReaderFactory;
+ }
+
+ /**
+ * Returns a descriptor with configured charset. If configured
+ * value is not null the {@link BaseExecutionService} will
+ * use the given charset to decode the process streams. When
+ * null the platform default will be used.
+ *
+ * Note that in the most common scenario of execution of OS native
+ * process you shouldn't need to set the charset. The platform default
+ * (which is the default used) is just the right choice.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param charset charset, null allowed
+ * @return this descriptor with configured charset
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor charset(@NullAllowed Charset charset) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ Charset getCharset() {
+ return charset;
+ }
+
+ /**
+ * Returns a descriptor with configured pre execution runnable. This
+ * runnable is executed before the external execution itself
+ * (when invoked by {@link BaseExecutionService#run()}).
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param preExecution pre execution runnable, null allowed
+ * @return new descriptor with configured pre execution runnable
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor preExecution(@NullAllowed Runnable preExecution) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ Runnable getPreExecution() {
+ return preExecution;
+ }
+
+ /**
+ * Returns a descriptor with configured post execution runnable. This
+ * runnable is executed after the external execution itself
+ * (when invoked by {@link BaseExecutionService#run()}).
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param postExecution post execution runnable, null allowed
+ * @return new descriptor with configured post execution runnable
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor postExecution(@NullAllowed ParametrizedRunnable postExecution) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ ParametrizedRunnable getPostExecution() {
+ return postExecution;
+ }
+
+ /**
+ * Returns a descriptor with configured factory for standard output
+ * processor. The factory is used by {@link BaseExecutionService} to create
+ * processor for standard output.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param outProcessorFactory factory for standard output processor,
+ * null allowed
+ * @return new descriptor with configured factory for additional
+ * processor to use for standard output
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor outProcessorFactory(@NullAllowed InputProcessorFactory outProcessorFactory) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ InputProcessorFactory getOutProcessorFactory() {
+ return outProcessorFactory;
+ }
+
+ /**
+ * Returns a descriptor with configured factory for standard error output
+ * processor. The factory is used by {@link BaseExecutionService} to create
+ * processor for standard error output.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param errProcessorFactory factory for standard error output processor,
+ * null allowed
+ * @return new descriptor with configured factory for additional
+ * processor to use for standard error output
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor errProcessorFactory(@NullAllowed InputProcessorFactory errProcessorFactory) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ InputProcessorFactory getErrProcessorFactory() {
+ return errProcessorFactory;
+ }
+
+ /**
+ * Returns a descriptor with configured factory for standard input reader.
+ * The factory is used by {@link BaseExecutionService} to create
+ * a reader providing input to the process.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param inReaderFactory factory for standard input reader,
+ * null allowed
+ * @return new descriptor with configured factory for reader to use
+ * for standard input
+ */
+ @NonNull
+ @CheckReturnValue
+ public BaseExecutionDescriptor inReaderFactory(@NullAllowed ReaderFactory inReaderFactory) {
+ return new BaseExecutionDescriptor(charset, preExecution, postExecution,
+ outProcessorFactory, errProcessorFactory, inReaderFactory);
+ }
+
+ ReaderFactory getInReaderFactory() {
+ return inReaderFactory;
+ }
+
+ /**
+ * Factory creating the input processor.
+ */
+ public interface InputProcessorFactory {
+
+ /**
+ * Creates and returns new input processor.
+ *
+ * @return new input processor
+ */
+ @NonNull
+ InputProcessor newInputProcessor();
+
+ }
+
+ /**
+ * Factory creating the reader.
+ */
+ public interface ReaderFactory {
+
+ /**
+ * Creates and returns new reader.
+ *
+ * @return new reader
+ */
+ Reader newReader();
+ }
+
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionService.java b/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionService.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/BaseExecutionService.java
@@ -0,0 +1,389 @@
+/*
+ * 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-2008 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.
+ */
+package org.netbeans.api.extexecution.base;
+
+import java.io.BufferedInputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.base.BaseExecutionDescriptor.InputProcessorFactory;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.InputProcessors;
+import org.netbeans.api.extexecution.base.input.InputReaderTask;
+import org.netbeans.api.extexecution.base.input.InputReaders;
+import org.netbeans.modules.extexecution.base.ProcessInputStream;
+import org.openide.util.Cancellable;
+import org.openide.util.RequestProcessor;
+
+/**
+ * Base Execution service provides the facility to execute a process.
+ *
+ * All processes launched by this class are terminated on VM exit (if
+ * these are not finished or terminated earlier).
+ *
+ *
+ *
+ * @author Petr Hejl
+ * @see #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.base.BaseExecutionDescriptor)
+ * @see BaseExecutionDescriptor
+ */
+public final class BaseExecutionService {
+
+ private static final Logger LOGGER = Logger.getLogger(BaseExecutionService.class.getName());
+
+ private static final Set RUNNING_PROCESSES = new HashSet();
+
+ private static final int EXECUTOR_SHUTDOWN_SLICE = 1000;
+
+ private static final ExecutorService EXECUTOR_SERVICE = new RequestProcessor(BaseExecutionService.class.getName(), Integer.MAX_VALUE);
+
+ static {
+
+ // shutdown hook
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+
+ @Override
+ public void run() {
+ EXECUTOR_SERVICE.shutdown();
+
+ synchronized (RUNNING_PROCESSES) {
+ for (Process process : RUNNING_PROCESSES) {
+ process.destroy();
+ }
+ }
+ }
+ });
+ }
+
+ private final Callable extends Process> processCreator;
+
+ private final BaseExecutionDescriptor descriptor;
+
+ public BaseExecutionService(Callable extends Process> processCreator,
+ BaseExecutionDescriptor descriptor) {
+ this.processCreator = processCreator;
+ this.descriptor = descriptor;
+ }
+
+ /**
+ * Creates new execution service. Service will wrap up the processes
+ * created by processCreator and will manage them.
+ *
+ * @param processCreator callable returning the process to wrap up
+ * @param descriptor descriptor describing the configuration of service
+ * @return new execution service
+ */
+ @NonNull
+ public static BaseExecutionService newService(@NonNull Callable extends Process> processCreator,
+ @NonNull BaseExecutionDescriptor descriptor) {
+ return new BaseExecutionService(processCreator, descriptor);
+ }
+
+ /**
+ * 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.
+ *
+ * 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.base.BaseExecutionDescriptor)}
+ * is invoked in order to create the process. If the process creation fails
+ * (throwing an exception) returned Future will throw
+ * {@link java.util.concurrent.ExecutionException} on {@link Future#get()}
+ * request.
+ *
+ * For details on execution control see {@link BaseExecutionDescriptor}.
+ *
+ * @return task representing the actual run, value representing result
+ * of the {@link Future} is exit code of the process
+ */
+ @NonNull
+ public Future run() {
+ final Reader in;
+ BaseExecutionDescriptor.ReaderFactory factory = descriptor.getInReaderFactory();
+ if (factory != null) {
+ in = factory.newReader();
+ } else {
+ in = null;
+ }
+
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+
+ Callable callable = new Callable() {
+ @Override
+ public Integer call() throws Exception {
+
+ boolean interrupted = false;
+ Process process = null;
+ Integer ret = null;
+ ExecutorService executor = null;
+
+ ProcessInputStream outStream = null;
+ ProcessInputStream errStream = null;
+
+ List tasks = new ArrayList();
+
+ try {
+ final Runnable pre = descriptor.getPreExecution();
+ if (pre != null) {
+ pre.run();
+ }
+
+ if (Thread.currentThread().isInterrupted()) {
+ return null;
+ }
+
+ process = processCreator.call();
+ synchronized (RUNNING_PROCESSES) {
+ RUNNING_PROCESSES.add(process);
+ }
+
+ if (Thread.currentThread().isInterrupted()) {
+ return null;
+ }
+
+ outStream = new ProcessInputStream(process, process.getInputStream());
+ errStream = new ProcessInputStream(process, process.getErrorStream());
+
+ executor = Executors.newFixedThreadPool(in != null ? 3 : 2);
+
+ Charset charset = descriptor.getCharset();
+ if (charset == null) {
+ charset = Charset.defaultCharset();
+ }
+
+ tasks.add(InputReaderTask.newDrainingTask(
+ InputReaders.forStream(new BufferedInputStream(outStream), charset),
+ createOutProcessor()));
+ tasks.add(InputReaderTask.newDrainingTask(
+ InputReaders.forStream(new BufferedInputStream(errStream), charset),
+ createErrProcessor()));
+ if (in != null) {
+ tasks.add(InputReaderTask.newTask(
+ InputReaders.forReader(in),
+ createInProcessor(process.getOutputStream(), charset)));
+ }
+ for (InputReaderTask task : tasks) {
+ executor.submit(task);
+ }
+
+ process.waitFor();
+ } catch (InterruptedException ex) {
+ LOGGER.log(Level.FINE, null, ex);
+ interrupted = true;
+ } catch (Throwable t) {
+ LOGGER.log(Level.INFO, null, t);
+ throw new WrappedException(t);
+ } finally {
+ try {
+ // fully evaluated - we want to clear interrupted status in any case
+ interrupted |= Thread.interrupted();
+
+ if (!interrupted) {
+ if (outStream != null) {
+ outStream.close(true);
+ }
+ if (errStream != null) {
+ errStream.close(true);
+ }
+ }
+
+ if (process != null) {
+ process.destroy();
+ synchronized (RUNNING_PROCESSES) {
+ RUNNING_PROCESSES.remove(process);
+ }
+
+ try {
+ ret = process.exitValue();
+ } catch (IllegalThreadStateException ex) {
+ LOGGER.log(Level.FINE, "Process not yet exited", ex);
+ }
+ }
+ } catch (Throwable t) {
+ LOGGER.log(Level.INFO, null, t);
+ throw new WrappedException(t);
+ } finally {
+ try {
+ cleanup(tasks, executor);
+
+ final ParametrizedRunnable post
+ = descriptor.getPostExecution();
+ if (post != null) {
+ post.run(ret);
+ }
+ } finally {
+ finishedLatch.countDown();
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+ };
+
+ final FutureTask current = new FutureTask(callable) {
+
+ @Override
+ protected void setException(Throwable t) {
+ if (t instanceof WrappedException) {
+ super.setException(((WrappedException) t).getCause());
+ } else {
+ super.setException(t);
+ }
+ }
+
+ };
+
+ EXECUTOR_SERVICE.execute(current);
+ return current;
+ }
+
+ private void cleanup(final List tasks, final ExecutorService processingExecutor) {
+ boolean interrupted = false;
+ if (processingExecutor != null) {
+ try {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Void run() {
+ processingExecutor.shutdown();
+ return null;
+ }
+ });
+ for (Cancellable cancellable : tasks) {
+ cancellable.cancel();
+ }
+ while (!processingExecutor.awaitTermination(EXECUTOR_SHUTDOWN_SLICE, TimeUnit.MILLISECONDS)) {
+ LOGGER.log(Level.INFO, "Awaiting processing finish");
+ }
+ } catch (InterruptedException ex) {
+ interrupted = true;
+ }
+ }
+
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private InputProcessor createOutProcessor() {
+ InputProcessor outProcessor = null;
+ InputProcessorFactory descriptorOutFactory = descriptor.getOutProcessorFactory();
+ if (descriptorOutFactory != null) {
+ outProcessor = descriptorOutFactory.newInputProcessor();
+ }
+
+ return outProcessor;
+ }
+
+ private InputProcessor createErrProcessor() {
+ InputProcessor errProcessor = null;
+ InputProcessorFactory descriptorErrFactory = descriptor.getErrProcessorFactory();
+ if (descriptorErrFactory != null) {
+ errProcessor = descriptorErrFactory.newInputProcessor();
+ }
+
+ return errProcessor;
+ }
+
+ private InputProcessor createInProcessor(OutputStream os, Charset charset) {
+ return InputProcessors.copying(new OutputStreamWriter(os, charset));
+ }
+
+ private static class WrappedException extends Exception {
+
+ public WrappedException(Throwable cause) {
+ super(cause);
+ }
+
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/Bundle.properties b/extexecution.base/src/org/netbeans/api/extexecution/base/Bundle.properties
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/Bundle.properties
@@ -0,0 +1,43 @@
+# 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-2006 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.
+
+LocalProcessBuilder=Builder creating local processes
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/Environment.java b/extexecution.base/src/org/netbeans/api/extexecution/base/Environment.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/Environment.java
@@ -0,0 +1,155 @@
+/*
+ * 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.base;
+
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.extexecution.base.EnvironmentAccessor;
+import org.netbeans.spi.extexecution.base.EnvironmentImplementation;
+import org.openide.util.Parameters;
+
+/**
+ * The class that provides an access to environment variables.
+ *
+ * @see ProcessBuilder#getEnvironment()
+ * @see EnvironmentImplementation
+ * @author Petr Hejl
+ */
+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.base/src/org/netbeans/api/extexecution/base/ParametrizedRunnable.java b/extexecution.base/src/org/netbeans/api/extexecution/base/ParametrizedRunnable.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/ParametrizedRunnable.java
@@ -0,0 +1,59 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base;
+
+/**
+ * The interface representing a runnable code accepting a parameter.
+ *
+ * @author Petr Hejl
+ * @param the type of required parameter
+ */
+public interface ParametrizedRunnable {
+
+ /**
+ * The runnable code itself.
+ *
+ * @param parameter the parameter
+ */
+ void run(T parameter);
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/ProcessBuilder.java b/extexecution.base/src/org/netbeans/api/extexecution/base/ProcessBuilder.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/ProcessBuilder.java
@@ -0,0 +1,405 @@
+/*
+ * 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.api.extexecution.base;
+
+import java.io.File;
+import java.io.IOException;
+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.base.ExternalProcessBuilder;
+import org.netbeans.modules.extexecution.base.ProcessBuilderAccessor;
+import org.netbeans.modules.extexecution.base.ProcessParametersAccessor;
+import org.netbeans.spi.extexecution.base.EnvironmentFactory;
+import org.netbeans.spi.extexecution.base.EnvironmentImplementation;
+import org.netbeans.spi.extexecution.base.ProcessBuilderImplementation;
+import org.netbeans.spi.extexecution.base.ProcessParameters;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.Parameters;
+
+/**
+ * Abstraction of process builders. You can freely configure the parameters
+ * and then create a process by calling the {@link #call()} method. You can
+ * also (re)configure the builder and spawn a different process.
+ *
+ * Note the API does not prescribe the actual meaning of {@link Process}.
+ * It may be local process, remote process or some other implementation.
+ *
+ * You can use the default implementation returned by {@link #getLocal()}
+ * for creating the local machine OS processes.
+ *
+ * Thread safety of this class depends on thread safety of
+ * 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 ProcessBuilderImplementation} is used and it is (including
+ * {@link EnvironmentImplementation}) thread safe and does not have any mutable
+ * configuration accessible via {@link ProcessBuilderImplementation#getLookup()}
+ * it is thread safe as well. Otherwise it is not thread safe.
+ *
+ * The synchronization mechanism used in this object is the {@link ProcessBuilderImplementation}
+ * object monitor.
+ *
+ * @author Petr Hejl
+ */
+// TODO proxy autoconfiguration optional via lookup
+public final class ProcessBuilder implements Callable, Lookup.Provider {
+
+ private final ProcessBuilderImplementation implementation;
+
+ private final Object lock;
+
+ private final String description;
+
+ /**GuardedBy("lock")*/
+ private String executable;
+
+ /**GuardedBy("lock")*/
+ private String workingDirectory;
+
+ /**GuardedBy("lock")*/
+ private final List arguments = new ArrayList();
+
+ /**GuardedBy("lock")*/
+ private boolean redirectErrorStream;
+
+ static {
+ ProcessBuilderAccessor.setDefault(new ProcessBuilderAccessor() {
+
+ @Override
+ public ProcessBuilder createProcessBuilder(ProcessBuilderImplementation impl, String description) {
+ return new ProcessBuilder(impl, description);
+ }
+ });
+ }
+
+ private ProcessBuilder(ProcessBuilderImplementation implementation2, String description) {
+ assert implementation2 != null;
+ this.implementation = implementation2;
+ this.description = description;
+
+ this.lock = implementation2;
+ }
+
+ /**
+ * Returns the {@link ProcessBuilder} creating the OS process on local
+ * machine. Returned implementation is thread safe.
+ * The returned builder also attempts to properly configure HTTP proxy
+ * for the process.
+ *
+ * @return the {@link ProcessBuilder} creating the OS process on local
+ * machine
+ */
+ public static ProcessBuilder getLocal() {
+ return new ProcessBuilder(new LocalProcessBuilder(),
+ NbBundle.getMessage(ProcessBuilder.class, "LocalProcessBuilder"));
+ }
+
+ /**
+ * Returns the human readable description of this builder.
+ *
+ * @return the human readable description of this builder
+ */
+ @NonNull
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the executable to run. There is no default value. The {@link #call()}
+ * methods throws {@link IllegalStateException} when there is no executable
+ * configured.
+ *
+ * @param executable the executable to run
+ */
+ public void setExecutable(@NonNull String executable) {
+ Parameters.notNull("executable", executable);
+
+ synchronized (lock) {
+ this.executable = executable;
+ }
+ }
+
+ /**
+ * Sets the working directory for the process created by subsequent call
+ * of {@link #call()}. The default value is implementation specific.
+ *
+ * @param workingDirectory the working directory of the process
+ */
+ public void setWorkingDirectory(@NullAllowed String workingDirectory) {
+ synchronized (lock) {
+ this.workingDirectory = workingDirectory;
+ }
+ }
+
+ /**
+ * Sets the arguments passed to the process created by subsequent call
+ * of {@link #call()}. By default there are no arguments.
+ *
+ * @param arguments the arguments passed to the process
+ */
+ public void setArguments(@NonNull List arguments) {
+ Parameters.notNull("arguments", arguments);
+
+ synchronized (lock) {
+ this.arguments.clear();
+ this.arguments.addAll(arguments);
+ }
+ }
+
+ /**
+ * Configures the error stream redirection. If true the error
+ * stream of process created by subsequent call of {@link #call()} method
+ * will be redirected to standard output stream.
+ *
+ * @param redirectErrorStream the error stream redirection
+ */
+ public void setRedirectErrorStream(boolean redirectErrorStream) {
+ synchronized (lock) {
+ this.redirectErrorStream = redirectErrorStream;
+ }
+ }
+
+ /**
+ * Returns the object for environment variables manipulation.
+ *
+ * @return the object for environment variables manipulation
+ */
+ @NonNull
+ public Environment getEnvironment() {
+ return implementation.getEnvironment();
+ }
+
+ /**
+ * Returns the associated {@link Lookup}. Extension point provided by
+ * {@link ProcessBuilderImplementation}.
+ *
+ * @return the associated {@link Lookup}.
+ * @see ProcessBuilderImplementation#getLookup()
+ */
+ @Override
+ public Lookup getLookup() {
+ if (implementation != null) {
+ return implementation.getLookup();
+ }
+ return Lookup.EMPTY;
+ }
+
+
+ /**
+ * Creates the new {@link Process} based on the properties configured
+ * in this builder.
+ *
+ * Actual behavior depends on the builder implementation, but it should
+ * respect all the properties configured on this builder.
+ *
+ * @see ProcessBuilderImplementation
+ * @return the new {@link Process} based on the properties configured
+ * in this builder
+ * @throws IOException if the process could not be created
+ * @throws IllegalStateException if there is no executable configured
+ * by {@link #setExecutable(java.lang.String)}
+ */
+ @NonNull
+ @Override
+ public Process call() throws IOException {
+ String currentExecutable;
+ String currentWorkingDirectory;
+ List currentArguments = new ArrayList();
+ Map currentVariables = new HashMap();
+ boolean currentRedirectErrorStream;
+
+ synchronized (lock) {
+ currentExecutable = executable;
+ currentWorkingDirectory = workingDirectory;
+ currentArguments.addAll(arguments);
+ currentRedirectErrorStream = redirectErrorStream;
+ currentVariables.putAll(getEnvironment().values());
+ }
+
+ if (currentExecutable == null) {
+ throw new IllegalStateException("The executable has not been configured");
+ }
+
+ ProcessParameters params = ProcessParametersAccessor.getDefault().createProcessParameters(
+ currentExecutable, currentWorkingDirectory, currentArguments,
+ currentRedirectErrorStream, currentVariables);
+ return implementation.createProcess(params);
+ }
+
+// /**
+// * Marks an object from which it is possible to get a {@link ProcessBuilder}.
+// */
+// 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 ProcessBuilderImplementation {
+
+ private final Environment environment = EnvironmentFactory.createEnvironment(
+ new LocalEnvironment(this, System.getenv()));
+
+ @Override
+ public Environment getEnvironment() {
+ return environment;
+ }
+
+ @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 : parameters.getArguments()) {
+ builder = builder.addArgument(argument);
+ }
+ builder = builder.redirectErrorStream(parameters.isRedirectErrorStream());
+
+ builder = builder.emptySystemVariables(true);
+ for (Map.Entry entry : parameters.getEnvironmentVariables().entrySet()) {
+ builder = builder.addEnvironmentVariable(entry.getKey(), entry.getValue());
+ }
+
+ 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.base/src/org/netbeans/api/extexecution/base/Processes.java b/extexecution.base/src/org/netbeans/api/extexecution/base/Processes.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/Processes.java
@@ -0,0 +1,82 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base;
+
+import java.util.Map;
+import org.netbeans.spi.extexecution.base.ProcessesImplementation;
+import org.openide.util.Lookup;
+
+/**
+ * The utility class for better processes handling.
+ *
+ * @author Petr Hejl
+ * @see ProcessesImplementation
+ */
+public final class Processes {
+
+ private Processes() {
+ super();
+ }
+
+ /**
+ * Kills the process passed as parameter and attempts to terminate
+ * all child processes in process tree.
+ *
+ * Any process running in environment containing the same variables
+ * with the same values as those passed in env (all of them)
+ * is supposed to be part of the process tree and may be killed.
+ *
+ * @param process process to kill
+ * @param environment map containing the variables and their values which the
+ * process must have to be considered being part of
+ * the tree to kill
+ */
+ public static void killTree(Process process, Map environment) {
+ ProcessesImplementation impl = Lookup.getDefault().lookup(ProcessesImplementation.class);
+ if (impl != null) {
+ impl.killTree(process, environment);
+ }
+
+ process.destroy();
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessor.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessor.java
@@ -0,0 +1,90 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Processes chars read by {@link InputReader}.
+ *
+ * When the implementation is used just by single InputReader it
+ * does not have to be thread safe.
+ *
+ * @author Petr Hejl
+ * @see InputReader
+ */
+public interface InputProcessor extends Closeable, AutoCloseable {
+
+ /**
+ * Processes the characters.
+ *
+ * @param chars characters to process
+ * @throws IOException if any processing error occurs
+ */
+ void processInput(@NonNull char[] chars) throws IOException;
+
+ /**
+ * Notifies the processor that it should reset its state.
+ *
+ * The circumstances when this method is called must be defined
+ * by the particular {@link InputReader}.
+ *
+ *
+ * For example reset is called by reader returned from
+ * {@link InputReaders#forFileInputProvider(org.netbeans.api.extexecution.base.input.InputReaders.FileInput.Provider) }
+ * when the provided file is changed.
+ *
+ *
+ * @throws IOException if error occurs while reseting
+ */
+ void reset() throws IOException;
+
+ /**
+ * Closes the processor releasing the resources held by it.
+ */
+ @Override
+ void close() throws IOException;
+
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessors.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessors.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputProcessors.java
@@ -0,0 +1,416 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.modules.extexecution.base.input.LineParsingHelper;
+import org.openide.util.Parameters;
+
+/**
+ * Factory methods for {@link InputProcessor} classes.
+ *
+ * @author Petr Hejl
+ */
+public final class InputProcessors {
+
+ private static final Logger LOGGER = Logger.getLogger(InputProcessors.class.getName());
+
+ private InputProcessors() {
+ super();
+ }
+
+ /**
+ * Returns the processor converting characters to the whole lines passing
+ * them to the given line processor.
+ *
+ * Any reset or close is delegated to the corresponding method
+ * of line processor.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param lineProcessor processor consuming parsed lines
+ * @return the processor converting characters to the whole lines
+ */
+ @NonNull
+ public static InputProcessor bridge(@NonNull LineProcessor lineProcessor) {
+ return new Bridge(lineProcessor);
+ }
+
+ /**
+ * Returns the processor acting as a proxy.
+ *
+ * Any action taken on this processor is distributed to all processors
+ * passed as arguments in the same order as they were passed to this method.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param processors processor to which the actions will be distributed
+ * @return the processor acting as a proxy
+ */
+ @NonNull
+ public static InputProcessor proxy(@NonNull InputProcessor... processors) {
+ return new ProxyInputProcessor(processors);
+ }
+
+ /**
+ * Returns the processor that writes every character passed for processing
+ * to the given writer.
+ *
+ * Reset action on the returned processor is noop. Processor closes the
+ * writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param writer processed characters will be written to this writer
+ * @return the processor that writes every character passed for processing
+ * to the given writer
+ */
+ @NonNull
+ public static InputProcessor copying(@NonNull Writer writer) {
+ return new CopyingInputProcessor(writer);
+ }
+
+ /**
+ * Returns the processor printing all characters passed for processing to
+ * the given writer.
+ *
+ * Reset action on the returned processor is noop. Processor closes the
+ * writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print received characters
+ * @return the processor printing all characters passed for processing to
+ * the given writer
+ */
+ @NonNull
+ public static InputProcessor printing(@NonNull PrintWriter out) {
+ return new PrintingInputProcessor(out);
+ }
+
+ /**
+ * Returns the processor that strips any
+ * ANSI escape sequences
+ * and passes the result to the delegate.
+ *
+ * Reset and close methods on the returned processor invokes
+ * the corresponding actions on delegate.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param delegate processor that will receive characters without control
+ * sequences
+ * @return the processor that strips any ANSI escape sequences and passes
+ * the result to the delegate
+ */
+ @NonNull
+ public static InputProcessor ansiStripping(@NonNull InputProcessor delegate) {
+ return new AnsiStrippingInputProcessor(delegate);
+ }
+
+ private static class Bridge implements InputProcessor {
+
+ private final LineProcessor lineProcessor;
+
+ private final LineParsingHelper helper = new LineParsingHelper();
+
+ private boolean closed;
+
+ public Bridge(LineProcessor lineProcessor) {
+ Parameters.notNull("lineProcessor", lineProcessor);
+
+ this.lineProcessor = lineProcessor;
+ }
+
+ @Override
+ public final void processInput(char[] chars) {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ String[] lines = helper.parse(chars);
+ for (String line : lines) {
+ lineProcessor.processLine(line);
+ }
+ }
+
+ @Override
+ public final void reset() {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ flush();
+ lineProcessor.reset();
+ }
+
+ @Override
+ public final void close() {
+ closed = true;
+
+ flush();
+ lineProcessor.close();
+ }
+
+ private void flush() {
+ String line = helper.getTrailingLine(true);
+ if (line != null) {
+ lineProcessor.processLine(line);
+ }
+ }
+ }
+
+ private static class ProxyInputProcessor implements InputProcessor {
+
+ private final List processors = new ArrayList();
+
+ private boolean closed;
+
+ public ProxyInputProcessor(InputProcessor... processors) {
+ for (InputProcessor processor : processors) {
+ if (processor != null) {
+ this.processors.add(processor);
+ }
+ }
+ }
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ for (InputProcessor processor : processors) {
+ processor.processInput(chars);
+ }
+ }
+
+ @Override
+ public void reset() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ for (InputProcessor processor : processors) {
+ processor.reset();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+
+ for (InputProcessor processor : processors) {
+ processor.close();
+ }
+ }
+ }
+
+ private static class PrintingInputProcessor implements InputProcessor {
+
+ private final PrintWriter out;
+
+ private final LineParsingHelper helper = new LineParsingHelper();
+
+ private boolean closed;
+
+ public PrintingInputProcessor(PrintWriter out) {
+ assert out != null;
+
+ this.out = out;
+ }
+
+ @Override
+ public void processInput(char[] chars) {
+ assert chars != null;
+
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ String[] lines = helper.parse(chars);
+ for (String line : lines) {
+ LOGGER.log(Level.FINEST, "{0}\\n", line);
+
+ out.println(line);
+ out.flush();
+ }
+
+ String line = helper.getTrailingLine(true);
+ if (line != null) {
+ LOGGER.log(Level.FINEST, line);
+
+ out.print(line);
+ out.flush();
+ }
+ }
+
+ @Override
+ public void reset() throws IOException {
+ // noop
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+
+ out.close();
+ }
+ }
+
+ private static class CopyingInputProcessor implements InputProcessor {
+
+ private final Writer writer;
+
+ private boolean closed;
+
+ public CopyingInputProcessor(Writer writer) {
+ this.writer = writer;
+ }
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ LOGGER.log(Level.FINEST, Arrays.toString(chars));
+ writer.write(chars);
+ writer.flush();
+ }
+
+ @Override
+ public void reset() {
+ // noop
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+
+ writer.close();
+ }
+ }
+
+ private static class AnsiStrippingInputProcessor implements InputProcessor {
+
+ private final InputProcessor delegate;
+
+ private boolean closed;
+
+ public AnsiStrippingInputProcessor(InputProcessor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ // FIXME optimize me
+ String sequence = new String(chars);
+ if (containsAnsiColors(sequence)) {
+ sequence = stripAnsiColors(sequence);
+ }
+ delegate.processInput(sequence.toCharArray());
+ }
+
+ @Override
+ public void reset() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ delegate.reset();
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+
+ delegate.close();
+ }
+
+ private static boolean containsAnsiColors(String sequence) {
+ // RSpec will color output with ANSI color sequence terminal escapes
+ return sequence.indexOf("\033[") != -1; // NOI18N
+ }
+
+ private static String stripAnsiColors(String sequence) {
+ StringBuilder sb = new StringBuilder(sequence.length());
+ int index = 0;
+ int max = sequence.length();
+ while (index < max) {
+ int nextEscape = sequence.indexOf("\033[", index); // NOI18N
+ if (nextEscape == -1) {
+ nextEscape = sequence.length();
+ }
+
+ for (int n = (nextEscape == -1) ? max : nextEscape; index < n; index++) {
+ sb.append(sequence.charAt(index));
+ }
+
+ if (nextEscape != -1) {
+ for (; index < max; index++) {
+ char c = sequence.charAt(index);
+ if (c == 'm') {
+ index++;
+ break;
+ }
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReader.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReader.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReader.java
@@ -0,0 +1,78 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.netbeans.api.annotations.common.NullAllowed;
+
+/**
+ * This interface represents abstraction for reading characters. It allows
+ * custom processing of such characters through the given processor.
+ *
+ * For safe usage in {@link InputReaderTask} implementation of this
+ * interface has to be responsive to interruption.
+ *
+ * @author Petr Hejl
+ */
+public interface InputReader extends Closeable, AutoCloseable {
+
+ /**
+ * Reads some input and process it through the processor (if any).
+ *
+ * Implementation of this method has to be non blocking
+ * for safe usage in {@link InputReaderTask}.
+ *
+ * @param processor consumer of read characters, may be null
+ * @return number of characters read
+ * @throws IOException if any read or process error occurs
+ */
+ int readInput(@NullAllowed InputProcessor processor) throws IOException;
+
+ /**
+ * Closes the reader releasing the resources held by it.
+ */
+ @Override
+ void close() throws IOException;
+
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaderTask.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaderTask.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaderTask.java
@@ -0,0 +1,271 @@
+/*
+ * 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.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Cancellable;
+import org.openide.util.Parameters;
+
+/**
+ * Task consuming data from the certain reader, processing them with the given
+ * processor.
+ *
+ * When exception occurs while the task is running the task is terminated.
+ * Task is responsive to interruption. InputReader is closed on finish (includes
+ * both cases throwing an exception and interruption).
+ *
+ * The {@link #run()} method can be executed just once.
+ *
+ * Task is not finished implicitly by reaching the end of the reader.
+ * The caller has to finish it either by interruption or explicit cancellation.
+ * Cancellation is preferred in situations where the interruption could make
+ * cleanup operations on {@link InputProcessor} impossible to happen.
+ *
+ *
+ *
+ * Sample usage - reading standard output of the process (throwing the data away):
+ *
+ *
+ * @author Petr Hejl
+ */
+public final class InputReaderTask implements Runnable, Cancellable {
+
+ private static final Logger LOGGER = Logger.getLogger(InputReaderTask.class.getName());
+
+ private static final int MIN_DELAY = 50;
+
+ private static final int MAX_DELAY = 300;
+
+ private static final int DELAY_INCREMENT = 50;
+
+ private final InputReader inputReader;
+
+ private final InputProcessor inputProcessor;
+
+ private final boolean draining;
+
+ private boolean cancelled;
+
+ private boolean running;
+
+ private InputReaderTask(InputReader inputReader, InputProcessor inputProcessor, boolean draining) {
+ this.inputReader = inputReader;
+ this.inputProcessor = inputProcessor;
+ this.draining = draining;
+ }
+
+ /**
+ * Creates a new task. The task will read the data from reader processing
+ * them through processor (if any) until interrupted or canceled.
+ *
+ * {@link InputReader} must be non blocking.
+ *
+ * @param reader data producer
+ * @param processor processor consuming the data, may be null
+ * @return task handling the read process
+ */
+ @NonNull
+ public static InputReaderTask newTask(@NonNull InputReader reader, @NullAllowed InputProcessor processor) {
+ Parameters.notNull("reader", reader);
+
+ return new InputReaderTask(reader, processor, false);
+ }
+
+ /**
+ * Creates the new task. The task will read the data from reader processing
+ * them through processor (if any). When interrupted or canceled task will
+ * try to read all the remaining available data before exiting.
+ *
+ * {@link InputReader} must be non blocking.
+ *
+ * @param reader data producer
+ * @param processor processor consuming the data, may be null
+ * @return task handling the read process
+ */
+ @NonNull
+ public static InputReaderTask newDrainingTask(@NonNull InputReader reader, @NullAllowed InputProcessor processor) {
+ Parameters.notNull("reader", reader);
+
+ return new InputReaderTask(reader, processor, true);
+ }
+
+ /**
+ * Task repeatedly reads the data from the InputReader, passing the content
+ * to InputProcessor (if any).
+ *
+ * It is not allowed to invoke run multiple times.
+ */
+ @Override
+ public void run() {
+ synchronized (this) {
+ if (running) {
+ throw new IllegalStateException("Already running task");
+ }
+ running = true;
+ }
+
+ boolean interrupted = false;
+ try {
+ long delay = MIN_DELAY;
+ int emptyReads = 0;
+
+ while (true) {
+ synchronized (this) {
+ if (Thread.currentThread().isInterrupted() || cancelled) {
+ interrupted = Thread.interrupted();
+ break;
+ }
+ }
+
+ int count = inputReader.readInput(inputProcessor);
+
+ // compute the delay based on how often we really get the data
+ if (count > 0) {
+ delay = MIN_DELAY;
+ emptyReads = 0;
+ } else {
+ // increase the delay only slowly - once for
+ // MAX_DELAY / DELAY_INCREMENT unsuccesfull read attempts
+ if (emptyReads > (MAX_DELAY / DELAY_INCREMENT)) {
+ emptyReads = 0;
+ delay = Math.min(delay + DELAY_INCREMENT, MAX_DELAY);
+ } else {
+ emptyReads++;
+ }
+ }
+
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ LOGGER.log(Level.FINEST, "Task {0} sleeping for {1} ms",
+ new Object[] {Thread.currentThread().getName(), delay});
+ }
+ try {
+ // give the producer some time to write the output
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ interrupted = true;
+ break;
+ }
+ }
+
+ synchronized (this) {
+ if (Thread.currentThread().isInterrupted() || cancelled) {
+ interrupted = Thread.interrupted();
+ }
+ }
+ } catch (Exception ex) {
+ LOGGER.log(Level.FINE, null, ex);
+ } finally {
+ // drain the rest
+ if (draining) {
+ try {
+ while (inputReader.readInput(inputProcessor) > 0) {
+ LOGGER.log(Level.FINE, "Draining the rest of the reader");
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.FINE, null, ex);
+ }
+ }
+
+ // perform cleanup
+ try {
+ if (inputProcessor != null) {
+ inputProcessor.close();
+ }
+ inputReader.close();
+ } catch (IOException ex) {
+ LOGGER.log(Level.INFO, null, ex);
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Cancels the task. If the task is not running or task is already canceled
+ * this is noop.
+ *
+ * @return true if the task was successfully canceled
+ */
+ @Override
+ public boolean cancel() {
+ synchronized (this) {
+ if (cancelled) {
+ return false;
+ }
+ cancelled = true;
+ return true;
+ }
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaders.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaders.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/InputReaders.java
@@ -0,0 +1,238 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.File;
+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;
+import org.netbeans.modules.extexecution.base.input.FileInputReader;
+import org.netbeans.modules.extexecution.base.input.DefaultInputReader;
+import org.openide.util.Parameters;
+
+/**
+ * Factory methods for {@link InputReader} classes.
+ *
+ * @author Petr Hejl
+ */
+public final class InputReaders {
+
+ private InputReaders() {
+ super();
+ }
+
+ /**
+ * Returns the input reader backed by the given reader.
+ *
+ * The client should not use the reader passed as argument anymore. When
+ * the returned input reader is closed reader passed as argument is closed
+ * respectively.
+ *
+ * Returned reader will never call reset on {@link InputProcessor} while
+ * reading.
+ *
+ * Returned reader is not thread safe so it can't be used in
+ * multiple instances of {@link InputReaderTask}.
+ *
+ * @param reader real source of the data
+ * @return input reader backed by the given reader
+ */
+ @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);
+ }
+
+ /**
+ * Returns the input reader backed by the given stream. To convert read
+ * bytes to characters specified charset is used.
+ *
+ * The client should not use the stream passed as argument anymore. When
+ * the returned input reader is closed stream is closed respectively.
+ *
+ * Returned reader will never call reset on {@link InputProcessor} while
+ * reading.
+ *
+ * Returned reader is not thread safe so it can't be used in
+ * multiple instances of {@link InputReaderTask}.
+ *
+ * @param stream real source of the data
+ * @param charset bytes to characters conversion charset
+ * @return input reader backed by the given stream
+ */
+ @NonNull
+ public static InputReader forStream(@NonNull InputStream stream, @NonNull Charset charset) {
+ Parameters.notNull("stream", stream);
+
+ return forReader(new InputStreamReader(stream, charset));
+ }
+
+ /**
+ * Returns the input reader for the given file. To convert read bytes
+ * to characters specified charset is used.
+ *
+ * Returned reader will never call reset on {@link InputProcessor} while
+ * reading.
+ *
+ * Returned reader is not thread safe so it can't be used in
+ * multiple instances of {@link InputReaderTask}.
+ *
+ * @param file file to read from
+ * @param charset bytes to characters conversion charset
+ * @return input reader for the given file
+ */
+ @NonNull
+ public static InputReader forFile(@NonNull File file, @NonNull Charset charset) {
+ Parameters.notNull("file", file);
+ Parameters.notNull("charset", charset);
+
+ final FileInput fileInput = new FileInput(file, charset);
+ return forFileInputProvider(new FileInput.Provider() {
+
+ @Override
+ public FileInput getFileInput() {
+ return fileInput;
+ }
+ });
+ }
+
+ /**
+ * Returns the input reader reading data from the given provider.
+ *
+ * This means that the actual file (and the corresponding charset) used
+ * can change during the processing. This is specifically useful for
+ * rotating log files.
+ *
+ * Before each read cycle reader invokes {@link FileInput.Provider#getFileInput()}
+ * to determine the actual file to read.
+ *
+ * When processing the input {@link InputProcessor#reset()} is called on
+ * each file change (when provided file input differs from the previous one).
+ *
+ * Returned reader is not thread safe so it can't be used in
+ * multiple instances of {@link InputReaderTask}.
+ *
+ * @param fileProvider provider used to get the file to process
+ * @return input reader for the given provider
+ */
+ @NonNull
+ public static InputReader forFileInputProvider(@NonNull FileInput.Provider fileProvider) {
+ Parameters.notNull("fileProvider", fileProvider);
+
+ return new FileInputReader(fileProvider);
+ }
+
+ /**
+ * Represents the file with associated charset for reading from it.
+ *
+ * This class is immutable.
+ */
+ public static final class FileInput {
+
+ private final File file;
+
+ private final Charset charset;
+
+ /**
+ * Creates the new input representing the given file.
+ *
+ * @param file file to represent
+ * @param charset associated charset
+ */
+ public FileInput(@NonNull File file, @NonNull Charset charset) {
+ Parameters.notNull("file", file);
+ Parameters.notNull("charset", charset);
+
+ this.file = file;
+ this.charset = charset;
+ }
+
+ /**
+ * Returns the charset for reading the file.
+ *
+ * @return the charset for reading the file
+ */
+ @NonNull
+ public Charset getCharset() {
+ return charset;
+ }
+
+ /**
+ * Returns the file represented by this input.
+ *
+ * @return the file represented by this input
+ */
+ @NonNull
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * Provides the file input.
+ *
+ * @see InputReaders#forFileInputProvider(org.netbeans.api.extexecution.base.input.InputReaders.FileInput.Provider)
+ */
+ public interface Provider {
+
+ /**
+ * Returns the file input to use or null if there is
+ * no file to read currently.
+ *
+ * @return the file input to use or null if there is
+ * no file to read currently
+ */
+ @CheckForNull
+ FileInput getFileInput();
+
+ }
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessor.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessor.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.Closeable;
+import org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Processes the lines fetched by {@link InputReader} usually with help
+ * of the {@link InputProcessors#bridge(LineProcessor)}.
+ *
+ * When the implementation is used just by single bridge it
+ * does not have to be thread safe.
+ *
+ * @author Petr Hejl
+ * @see InputProcessors#bridge(LineProcessor)
+ * @see InputReader
+ */
+public interface LineProcessor extends Closeable, AutoCloseable {
+
+ /**
+ * Processes the line.
+ *
+ * @param line the line to process
+ */
+ void processLine(@NonNull String line);
+
+ /**
+ * Notifies the processor that it should reset its state.
+ *
+ * The circumstances when this method is called must be defined by
+ * the code using this class.
+ *
+ * For example processor created with
+ * {@link InputProcessors#bridge(LineProcessor)} delegates any call
+ * to {@link InputProcessor#reset()} to this method.
+ *
+ */
+ void reset();
+
+ /**
+ * Closes the processor releasing the resources held by it.
+ */
+ @Override
+ void close();
+
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessors.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessors.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/LineProcessors.java
@@ -0,0 +1,257 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Factory methods for {@link LineProcessor} classes.
+ *
+ * Note that main difference between {@link InputProcessor} and
+ * {@link LineProcessor} is that LineProcessor always process whole lines.
+ *
+ * @author Petr Hejl
+ * @see InputProcessors#bridge(org.netbeans.api.extexecution.base.input.LineProcessor)
+ */
+public final class LineProcessors {
+
+ private static final Logger LOGGER = Logger.getLogger(LineProcessors.class.getName());
+
+ private LineProcessors() {
+ super();
+ }
+
+ /**
+ * Returns the processor acting as a proxy.
+ *
+ * Any action taken on this processor is distributed to all processors
+ * passed as arguments in the same order as they were passed to this method.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param processors processor to which the actions will be distributed
+ * @return the processor acting as a proxy
+ */
+ @NonNull
+ public static LineProcessor proxy(@NonNull LineProcessor... processors) {
+ return new ProxyLineProcessor(processors);
+ }
+
+ /**
+ * Returns the processor printing all lines passed for processing to
+ * the given output writer.
+ *
+ * Processor closes the output writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print received lines
+ * @return the processor printing all lines passed for processing to
+ * the given output writer
+ */
+ @NonNull
+ public static LineProcessor printing(@NonNull PrintWriter out) {
+ return new PrintingLineProcessor(out);
+ }
+
+ /**
+ * Returns the processor that will wait for the line matching the pattern,
+ * decreasing the latch when such line appears for the first time.
+ *
+ * Reset action on the returned processor is noop.
+ *
+ * Returned processor is thread safe.
+ *
+ * @param pattern pattern that line must match in order decrease the latch
+ * @param latch latch to decrease when the line matching the pattern appears
+ * for the first time
+ * @return the processor that will wait for the line matching the pattern,
+ * decreasing the latch when such line appears for the first time
+ */
+ @NonNull
+ public static LineProcessor patternWaiting(@NonNull Pattern pattern, @NonNull CountDownLatch latch) {
+ return new WaitingLineProcessor(pattern, latch);
+ }
+
+ private static class ProxyLineProcessor implements LineProcessor {
+
+ private final List processors = new ArrayList();
+
+ private boolean closed;
+
+ public ProxyLineProcessor(LineProcessor... processors) {
+ for (LineProcessor processor : processors) {
+ if (processor != null) {
+ this.processors.add(processor);
+ }
+ }
+ }
+
+ @Override
+ public void processLine(String line) {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ for (LineProcessor processor : processors) {
+ processor.processLine(line);
+ }
+ }
+
+ @Override
+ public void reset() {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ for (LineProcessor processor : processors) {
+ processor.reset();
+ }
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+
+ for (LineProcessor processor : processors) {
+ processor.close();
+ }
+ }
+ }
+
+ private static class PrintingLineProcessor implements LineProcessor {
+
+ private final PrintWriter out;
+
+ private boolean closed;
+
+ public PrintingLineProcessor(PrintWriter out) {
+ assert out != null;
+
+ this.out = out;
+ }
+
+ @Override
+ public void processLine(String line) {
+ assert line != null;
+
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ LOGGER.log(Level.FINEST, line);
+
+ out.println(line);
+ out.flush();
+ }
+
+ @Override
+ public void reset() {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+
+ out.flush();
+ out.close();
+ }
+ }
+
+ private static class WaitingLineProcessor implements LineProcessor {
+
+ private final Pattern pattern;
+
+ private final CountDownLatch latch;
+
+ /**GuardedBy("this")*/
+ private boolean processed;
+
+ /**GuardedBy("this")*/
+ private boolean closed;
+
+ public WaitingLineProcessor(Pattern pattern, CountDownLatch latch) {
+ assert pattern != null;
+ assert latch != null;
+
+ this.pattern = pattern;
+ this.latch = latch;
+ }
+
+ @Override
+ public synchronized void processLine(String line) {
+ assert line != null;
+
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ if (!processed && pattern.matcher(line).matches()) {
+ latch.countDown();
+ processed = true;
+ }
+ }
+
+ @Override
+ public synchronized void reset() {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ closed = true;
+ }
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/input/package-info.java b/extexecution.base/src/org/netbeans/api/extexecution/base/input/package-info.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/input/package-info.java
@@ -0,0 +1,53 @@
+/*
+ * 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 support API for processing the growing streams or files. The API
+ * provides classes for automated processing of such inputs. The processing
+ * is character or line based.
+ *
+ * @see org.netbeans.api.extexecution.base.input.InputReaderTask
+ */
+package org.netbeans.api.extexecution.base.input;
+
diff --git a/extexecution.base/src/org/netbeans/api/extexecution/base/package-info.java b/extexecution.base/src/org/netbeans/api/extexecution/base/package-info.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/api/extexecution/base/package-info.java
@@ -0,0 +1,51 @@
+/*
+ * 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 execution of an external process.
+ *
+ * @see org.netbeans.api.extexecution.base.BaseExecutionService
+ */
+package org.netbeans.api.extexecution.base;
+
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/EnvironmentAccessor.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/EnvironmentAccessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/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.base;
+
+import org.netbeans.api.extexecution.base.Environment;
+import org.netbeans.spi.extexecution.base.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.base/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilder.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilder.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilder.java
@@ -0,0 +1,576 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.base;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+import java.util.regex.Pattern;
+import org.netbeans.api.annotations.common.CheckReturnValue;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.NbPreferences;
+import org.openide.util.Parameters;
+import org.openide.util.BaseUtilities;
+
+/**
+ * Utility class to make the local external process creation easier.
+ *
+ * Builder handle command, working directory, PATH variable and HTTP proxy.
+ *
+ * This class is immutable.
+ *
+ * Also see {@link ProcessBuilder#getLocal()}.
+ *
+ * @author Petr Hejl
+ * @see #call()
+ */
+public final class ExternalProcessBuilder implements Callable {
+
+ private static final Logger LOGGER = Logger.getLogger(ExternalProcessBuilder.class.getName());
+
+ private static final Pattern ESCAPED_PATTERN = Pattern.compile("\".*\""); // NOI18N
+
+ // FIXME: get rid of those proxy constants as soon as some NB Proxy API is available
+ private static final String USE_PROXY_AUTHENTICATION = "useProxyAuthentication"; // NOI18N
+
+ private static final String PROXY_AUTHENTICATION_USERNAME = "proxyAuthenticationUsername"; // NOI18N
+
+ private static final String PROXY_AUTHENTICATION_PASSWORD = "proxyAuthenticationPassword"; // NOI18N
+
+ private final String executable;
+
+ private final File workingDirectory;
+
+ private final boolean redirectErrorStream;
+
+ private final List arguments = new ArrayList();
+
+ private final List paths = new ArrayList();
+
+ 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.
+ *
+ * @param executable executable to run
+ */
+ public ExternalProcessBuilder(@NonNull String executable) {
+ this(new BuilderData(executable));
+ }
+
+ private ExternalProcessBuilder(BuilderData builder) {
+ this.executable = builder.executable;
+ this.workingDirectory = builder.workingDirectory;
+ this.redirectErrorStream = builder.redirectErrorStream;
+ this.arguments.addAll(builder.arguments);
+ this.paths.addAll(builder.paths);
+ this.envVariables.putAll(builder.envVariables);
+ this.emptySystemVariables = builder.emptySystemVariables;
+ }
+
+ /**
+ * Returns a builder with configured working directory. Process
+ * subsequently created by the {@link #call()} method on returned builder
+ * will be executed with this directory as current working dir.
+ *
+ * The default value is undefined. Note that in such case each process has
+ * working directory corresponding to the value of user.dir
+ * system property.
+ *
+ * All other properties of the returned builder are inherited from
+ * this.
+ *
+ * @param workingDirectory working directory
+ * @return new builder with configured working directory
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExternalProcessBuilder workingDirectory(@NonNull File workingDirectory) {
+ Parameters.notNull("workingDirectory", workingDirectory);
+
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.workingDirectory(workingDirectory));
+ }
+
+ /**
+ * Returns a builder with configured error stream redirection. If configured
+ * value is true process subsequently created by
+ * the {@link #call()} method on returned builder will redirect the error
+ * stream to the standard output stream.
+ *
+ * The default value is false.
+ *
+ * All other properties of the returned builder are inherited from
+ * this.
+ *
+ * @param redirectErrorStream if true error stream will be
+ * redirected to standard output
+ * @return new builder with configured error stream redirection
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExternalProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.redirectErrorStream(redirectErrorStream));
+ }
+
+ /**
+ * Returns a builder with additional path in PATH variable.
+ *
+ * In the group of paths added by this call the last added path will
+ * be the first one in the PATH variable.
+ *
+ * By default no additional paths are added to PATH variable.
+ *
+ * All other properties of the returned builder are inherited from
+ * this.
+ *
+ * @param path path to add to PATH variable
+ * @return new builder with additional path in PATH variable
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExternalProcessBuilder prependPath(@NonNull File path) {
+ Parameters.notNull("path", path);
+
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.prependPath(path));
+ }
+
+ /**
+ * Returns a builder with additional argument for the command. Arguments
+ * are passed to executable in the same order in which they are added.
+ *
+ * By default no additional arguments are passed to executable.
+ *
+ * All other properties of the returned builder are inherited from
+ * this.
+ *
+ * If there is a need to parse arguments already provided as one big
+ * string the method that can help is
+ * {@link Utilities#parseParameters(java.lang.String)}.
+ *
+ *
+ * @param argument command argument to add
+ * @return new builder with additional argument for the command
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExternalProcessBuilder addArgument(@NonNull String argument) {
+ Parameters.notNull("argument", argument);
+
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.addArgument(argument));
+ }
+
+ /**
+ * Returns a builder with additional environment variable for the command.
+ *
+ * By default no additional environment variables are configured.
+ *
+ * All other properties of the returned builder are inherited from
+ * this.
+ *
+ * @param name name of the variable
+ * @param value value of the variable
+ * @return new builder with additional environment variable for the command
+ * @see #call()
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExternalProcessBuilder addEnvironmentVariable(@NonNull String name, @NonNull String value) {
+ Parameters.notNull("name", name);
+ Parameters.notNull("value", value);
+
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.addEnvironmentVariable(name, value));
+ }
+
+ /**
+ * Creates the new {@link Process} based on the properties configured
+ * in this builder. Created process will try to kill all its children on
+ * call to {@link Process#destroy()}.
+ *
+ * Process is created by executing the executable with configured arguments.
+ * If custom working directory is specified it is used otherwise value
+ * of system property user.dir is used as working dir.
+ *
+ * Environment variables are prepared in following way:
+ *
+ *
Get table of system environment variables.
+ *
Put all environment variables configured by
+ * {@link #addEnvironmentVariable(java.lang.String, java.lang.String)}.
+ * This rewrites system variables if conflict occurs.
+ *
Get PATH variable and append all paths added
+ * by {@link #prependPath(java.io.File)}. The order of paths in PATH
+ * variable is reversed to order of addition (the last added is the first
+ * one in PATH). Original content of PATH follows
+ * the added content.
+ *
If neither http_proxy nor HTTP_PROXY
+ * environment variable is set then HTTP proxy settings configured in the
+ * IDE are stored as http_proxy environment variable
+ * (the format of the value is http://username:password@host:port).
+ *
+ * @return the new {@link Process} based on the properties configured
+ * in this builder
+ * @throws IOException if the process could not be created
+ */
+ @NonNull
+ @Override
+ public Process call() throws IOException {
+ List commandList = new ArrayList();
+
+ if (BaseUtilities.isWindows() && !ESCAPED_PATTERN.matcher(executable).matches()) {
+ commandList.add(escapeString(executable));
+ } else {
+ commandList.add(executable);
+ }
+
+ List args = buildArguments();
+ commandList.addAll(args);
+
+ java.lang.ProcessBuilder pb = new java.lang.ProcessBuilder(commandList.toArray(new String[commandList.size()]));
+ if (workingDirectory != null) {
+ pb.directory(workingDirectory);
+ }
+
+ Map pbEnv = pb.environment();
+ 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);
+ adjustProxy(pb);
+ pb.redirectErrorStream(redirectErrorStream);
+ logProcess(Level.FINE, pb);
+ WrapperProcess wp = new WrapperProcess(pb.start(), uuid);
+ return wp;
+ }
+
+ public ExternalProcessBuilder emptySystemVariables(boolean emptySystemVariables) {
+ BuilderData builder = new BuilderData(this);
+ return new ExternalProcessBuilder(builder.emptySystemVariables(emptySystemVariables));
+ }
+
+ /**
+ * Logs the given pb using the given level.
+ *
+ * @param pb the ProcessBuilder to log.
+ * @param level the level for logging.
+ */
+ private void logProcess(final Level level, final java.lang.ProcessBuilder pb) {
+
+ if (!LOGGER.isLoggable(level)) {
+ return;
+ }
+
+ File dir = pb.directory();
+ String basedir = dir == null ? "" : "(basedir: " + dir.getAbsolutePath() + ") "; //NOI18N
+
+ StringBuilder command = new StringBuilder();
+ for (Iterator it = pb.command().iterator(); it.hasNext();) {
+ command.append(it.next());
+ if (it.hasNext()) {
+ command.append(' '); //NOI18N
+ }
+ }
+
+ LOGGER.log(level, "Running: " + basedir + '"' + command.toString() + '"'); //NOI18N
+ LOGGER.log(level, "Environment: " + pb.environment()); //NOI18N
+ }
+
+ // package level for unit testing
+ Map buildEnvironment(Map original) {
+ Map ret = new HashMap(original);
+ ret.putAll(envVariables);
+
+ // Find PATH environment variable - on Windows it can be some other
+ // case and we should use whatever it has.
+ String pathName = getPathName(original);
+
+ // TODO use StringBuilder
+ String currentPath = ret.get(pathName);
+
+ if (currentPath == null) {
+ currentPath = "";
+ }
+
+ for (File path : paths) {
+ currentPath = path.getAbsolutePath().replace(" ", "\\ ") //NOI18N
+ + File.pathSeparator + currentPath;
+ }
+
+ if (!"".equals(currentPath.trim())) {
+ ret.put(pathName, currentPath);
+ }
+ return ret;
+ }
+
+
+ // package level for unit testing
+ List buildArguments() {
+ if (!BaseUtilities.isWindows()) {
+ return new ArrayList(arguments);
+ }
+ List result = new ArrayList(arguments.size());
+ for (String arg : arguments) {
+ if (arg != null && !ESCAPED_PATTERN.matcher(arg).matches()) {
+ result.add(escapeString(arg));
+ } else {
+ result.add(arg);
+ }
+ }
+ return result;
+ }
+
+ public 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);
+ }
+ }
+
+ public 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 (BaseUtilities.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
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ boolean hasSpace = false;
+ final int slen = s.length();
+ char c;
+
+ for (int i = 0; i < slen; i++) {
+ c = s.charAt(i);
+
+ if (Character.isWhitespace(c)) {
+ hasSpace = true;
+ sb.append(c);
+
+ continue;
+ }
+ sb.append(c);
+ }
+
+ if (hasSpace) {
+ sb.insert(0, '"'); // NOI18N
+ sb.append('"'); // NOI18N
+ }
+ return sb.toString();
+ }
+
+ private void adjustProxy(java.lang.ProcessBuilder pb) {
+ String proxy = getNetBeansHttpProxy();
+ if (proxy != null) {
+ Map env = pb.environment();
+ if ((env.get("HTTP_PROXY") == null) && (env.get("http_proxy") == null)) { // NOI18N
+ env.put("HTTP_PROXY", proxy); // NOI18N
+ env.put("http_proxy", proxy); // NOI18N
+ }
+ // PENDING - what if proxy was null so the user has TURNED off
+ // proxies while there is still an environment variable set - should
+ // we honor their environment, or honor their NetBeans proxy
+ // settings (e.g. unset HTTP_PROXY in the environment before
+ // launching plugin?
+ }
+ }
+
+ /**
+ * FIXME: get rid of the whole method as soon as some NB Proxy API is
+ * available.
+ */
+ private static String getNetBeansHttpProxy() {
+ // FIXME use ProxySelector
+
+ String host = System.getProperty("http.proxyHost"); // NOI18N
+
+ if (host == null) {
+ return null;
+ }
+
+ String portHttp = System.getProperty("http.proxyPort"); // NOI18N
+ int port;
+
+ try {
+ port = Integer.parseInt(portHttp);
+ } catch (NumberFormatException e) {
+ port = 8080;
+ }
+
+ Preferences prefs = NbPreferences.root().node("org/netbeans/core"); // NOI18N
+ boolean useAuth = prefs.getBoolean(USE_PROXY_AUTHENTICATION, false);
+ String auth = "";
+ if (useAuth) {
+ auth = prefs.get(PROXY_AUTHENTICATION_USERNAME, "") + ":" + prefs.get(PROXY_AUTHENTICATION_PASSWORD, "") + '@'; // NOI18N
+ }
+
+ // Gem requires "http://" in front of the port name if it's not already there
+ if (host.indexOf(':') == -1) {
+ host = "http://" + auth + host; // NOI18N
+ }
+
+ return host + ":" + port; // NOI18N
+ }
+
+ private static class BuilderData {
+
+ private final String executable;
+
+ private File workingDirectory;
+
+ private boolean redirectErrorStream;
+
+ private List arguments = new ArrayList();
+
+ private List paths = new ArrayList();
+
+ private Map envVariables = new HashMap();
+
+ private boolean emptySystemVariables;
+
+ public BuilderData(String executable) {
+ this.executable = executable;
+ }
+
+ public BuilderData(ExternalProcessBuilder builder) {
+ this.executable = builder.executable;
+ this.workingDirectory = builder.workingDirectory;
+ this.redirectErrorStream = builder.redirectErrorStream;
+ this.arguments.addAll(builder.arguments);
+ this.paths.addAll(builder.paths);
+ this.envVariables.putAll(builder.envVariables);
+ this.emptySystemVariables = builder.emptySystemVariables;
+ }
+
+ public BuilderData workingDirectory(File workingDirectory) {
+ assert workingDirectory != null;
+
+ this.workingDirectory = workingDirectory;
+ return this;
+ }
+
+ public BuilderData redirectErrorStream(boolean redirectErrorStream) {
+ this.redirectErrorStream = redirectErrorStream;
+ return this;
+ }
+
+ public BuilderData prependPath(File path) {
+ assert path != null;
+
+ paths.add(path);
+ return this;
+ }
+
+ public BuilderData addArgument(String argument) {
+ assert argument != null;
+
+ arguments.add(argument);
+ return this;
+ }
+
+ public BuilderData addEnvironmentVariable(String name, String value) {
+ assert name != null;
+ assert value != null;
+
+ envVariables.put(name, value);
+ return this;
+ }
+
+ public BuilderData emptySystemVariables(boolean emptySystemVariables) {
+ this.emptySystemVariables = emptySystemVariables;
+ return this;
+ }
+ }
+
+
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessBuilderAccessor.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessBuilderAccessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessBuilderAccessor.java
@@ -0,0 +1,81 @@
+/*
+ * 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.modules.extexecution.base;
+
+import org.netbeans.spi.extexecution.base.ProcessBuilderImplementation;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public abstract class ProcessBuilderAccessor {
+
+ private static volatile ProcessBuilderAccessor DEFAULT;
+
+ public static ProcessBuilderAccessor getDefault() {
+ ProcessBuilderAccessor a = DEFAULT;
+ if (a != null) {
+ return a;
+ }
+
+ // invokes static initializer of ProcessBuilder.class
+ // that will assign value to the DEFAULT field above
+ Class c = org.netbeans.api.extexecution.base.ProcessBuilder.class;
+ try {
+ Class.forName(c.getName(), true, c.getClassLoader());
+ } catch (ClassNotFoundException ex) {
+ assert false : ex;
+ }
+ return DEFAULT;
+ }
+
+ public static void setDefault(ProcessBuilderAccessor accessor) {
+ if (DEFAULT != null) {
+ throw new IllegalStateException();
+ }
+
+ DEFAULT = accessor;
+ }
+
+ public abstract org.netbeans.api.extexecution.base.ProcessBuilder createProcessBuilder(
+ ProcessBuilderImplementation impl, String description);
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessInputStream.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessInputStream.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessInputStream.java
@@ -0,0 +1,196 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.base;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public final class ProcessInputStream extends FilterInputStream {
+
+ private static final Logger LOGGER = Logger.getLogger(ProcessInputStream.class.getName());
+
+ private final Process process;
+
+ private byte[] buffer;
+
+ private int position;
+
+ private boolean closed;
+
+ private boolean exhausted;
+
+ public ProcessInputStream(Process process, InputStream in) {
+ super(in);
+ this.process = process;
+ }
+
+ @Override
+ public synchronized int available() throws IOException {
+ if (buffer != null && position < buffer.length) {
+ return buffer.length - position;
+ } else if (closed) {
+ if (!exhausted) {
+ exhausted = true;
+ return 0;
+ } else {
+ throw new IOException("Already closed stream");
+ }
+ }
+ return super.available();
+ }
+
+ @Override
+ public synchronized void close() throws IOException {
+ if (!closed) {
+ close(false);
+ }
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ // noop
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public synchronized int read() throws IOException {
+ if (buffer != null && position < buffer.length) {
+ return buffer[position++];
+ } else if (closed) {
+ if (!exhausted) {
+ exhausted = true;
+ return -1;
+ } else {
+ throw new IOException("Already closed stream");
+ }
+ }
+ return super.read();
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public synchronized int read(byte[] b, int off, int len) throws IOException {
+ if (buffer != null) {
+ int available = buffer.length - position;
+ int size = Math.min(len, available);
+ System.arraycopy(buffer, position, b, off, size);
+ position += size;
+ return size;
+ } else if (closed) {
+ if (!exhausted) {
+ exhausted = true;
+ return -1;
+ } else {
+ throw new IOException("Already closed stream");
+ }
+ }
+ return super.read(b, off, len);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ // noop
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return 0;
+ }
+
+ public synchronized void close(boolean drain) throws IOException {
+ closed = true;
+
+ if (drain) {
+ LOGGER.log(Level.FINE, "Draining process stream");
+
+ boolean running = false;
+ try {
+ process.exitValue();
+ } catch (IllegalThreadStateException ex) {
+ running = true;
+ }
+
+ if (running) {
+ LOGGER.log(Level.FINE, "Process is still running");
+ }
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ if (running) {
+ while (super.available() > 0) {
+ os.write(super.read());
+ }
+ } else {
+ int read;
+ // FIXME this occasionaly block forever on Vista :(
+ while ((read = super.read()) >= 0) {
+ os.write(read);
+ }
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.FINE, null, ex);
+ }
+
+ buffer = os.toByteArray();
+ LOGGER.log(Level.FINE, "Read {0} bytes from stream", buffer.length);
+ }
+
+ super.close();
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessParametersAccessor.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/ProcessParametersAccessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/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.base;
+
+import java.util.List;
+import java.util.Map;
+import org.netbeans.spi.extexecution.base.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.base/src/org/netbeans/modules/extexecution/base/WrapperProcess.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/WrapperProcess.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/WrapperProcess.java
@@ -0,0 +1,96 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.base;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import org.netbeans.api.extexecution.base.Processes;
+
+/**
+ *
+ * @author mkleint
+ */
+public class WrapperProcess extends Process {
+
+ public static final String KEY_UUID = "NB_EXEC_EXTEXECUTION_PROCESS_UUID"; //NOI18N
+ private final String uuid;
+
+ private final Process del;
+
+ public WrapperProcess(Process delegate, String uuid) {
+ this.del = delegate;
+ this.uuid = uuid;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return del.getOutputStream();
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return del.getInputStream();
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ return del.getErrorStream();
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ return del.waitFor();
+ }
+
+ @Override
+ public int exitValue() {
+ return del.exitValue();
+ }
+
+ @Override
+ public void destroy() {
+ Processes.killTree(del, Collections.singletonMap(KEY_UUID, uuid));
+ }
+
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/input/DefaultInputReader.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/DefaultInputReader.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/DefaultInputReader.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package org.netbeans.modules.extexecution.base.input;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.InputReader;
+
+/**
+ * This class is NotThreadSafe.
+ *
+ * @author Petr.Hejl
+ */
+public class DefaultInputReader implements InputReader {
+
+ private static final Logger LOGGER = Logger.getLogger(DefaultInputReader.class.getName());
+
+ private static final int BUFFER_SIZE = 512;
+
+ private final Reader reader;
+
+ private final char[] buffer;
+
+ private final boolean greedy;
+
+ private boolean closed;
+
+ public DefaultInputReader(Reader reader, boolean greedy) {
+ assert reader != null;
+
+ this.reader = new BufferedReader(reader);
+ this.greedy = greedy;
+ this.buffer = new char[greedy ? BUFFER_SIZE * 2 : BUFFER_SIZE];
+ }
+
+ @Override
+ public int readInput(InputProcessor inputProcessor) throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed reader");
+ }
+
+ if (!reader.ready()) {
+ return 0;
+ }
+
+ int fetched = 0;
+ // TODO optimization possible
+ StringBuilder builder = new StringBuilder();
+ do {
+ int size = reader.read(buffer);
+ if (size > 0) {
+ builder.append(buffer, 0, size);
+ fetched += size;
+ }
+ } while (reader.ready() && greedy);
+
+ if (inputProcessor != null && fetched > 0) {
+ inputProcessor.processInput(builder.toString().toCharArray());
+ }
+
+ return fetched;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ reader.close();
+ LOGGER.log(Level.FINEST, "Reader closed");
+ }
+
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/input/FileInputReader.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/FileInputReader.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/FileInputReader.java
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+
+package org.netbeans.modules.extexecution.base.input;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.InputReader;
+import org.netbeans.api.extexecution.base.input.InputReaders;
+
+/**
+ * This class is NotThreadSafe.
+ *
+ * @author Petr Hejl
+ */
+public class FileInputReader implements InputReader {
+
+ private static final Logger LOGGER = Logger.getLogger(FileInputReader.class.getName());
+
+ private static final int BUFFER_SIZE = 512;
+
+ private final InputReaders.FileInput.Provider fileProvider;
+
+ private final char[] buffer = new char[BUFFER_SIZE];
+
+ private InputReaders.FileInput currentFile;
+
+ private Reader reader;
+
+ private long fileLength;
+
+ private boolean closed;
+
+ public FileInputReader(InputReaders.FileInput.Provider fileProvider) {
+ assert fileProvider != null;
+
+ this.fileProvider = fileProvider;
+ }
+
+ @Override
+ public int readInput(InputProcessor inputProcessor) {
+ if (closed) {
+ throw new IllegalStateException("Already closed reader");
+ }
+
+ int fetched = 0;
+ try {
+ InputReaders.FileInput file = fileProvider.getFileInput();
+
+ if ((currentFile != file && (currentFile == null || !currentFile.equals(file)))
+ || fileLength > currentFile.getFile().length() || reader == null) {
+
+ if (reader != null) {
+ reader.close();
+ }
+
+ currentFile = file;
+
+ if (currentFile != null && currentFile.getFile().exists()
+ && currentFile.getFile().canRead()) {
+
+ reader = new BufferedReader(new InputStreamReader(
+ new FileInputStream(currentFile.getFile()), currentFile.getCharset()));
+ }
+ if (fileLength > 0) {
+ inputProcessor.reset();
+ }
+ fileLength = 0;
+ }
+
+ if (reader == null) {
+ return fetched;
+ }
+
+ int size = reader.read(buffer);
+ if (size > 0) {
+ fileLength += size;
+ fetched += size;
+
+ if (inputProcessor != null) {
+ char[] toProcess = new char[size];
+ System.arraycopy(buffer, 0, toProcess, 0, size);
+ inputProcessor.processInput(toProcess);
+ }
+ }
+ } catch (Exception ex) {
+ LOGGER.log(Level.INFO, null, ex);
+ // we will try the next loop (if any)
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException iex) {
+ LOGGER.log(Level.FINE, null, ex);
+ }
+ }
+ }
+
+ return fetched;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed = true;
+ if (reader != null) {
+ reader.close();
+ reader = null;
+ }
+ }
+
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/input/LineParsingHelper.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/LineParsingHelper.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/LineParsingHelper.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package org.netbeans.modules.extexecution.base.input;
+
+import java.nio.CharBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is NotThreadSafe.
+ *
+ * @author Petr Hejl
+ */
+public final class LineParsingHelper {
+
+ private String trailingLine;
+
+ public LineParsingHelper() {
+ super();
+ }
+
+ public String[] parse(char[] buffer) {
+ return parse(buffer, 0, buffer.length);
+ }
+
+ public String[] parse(char[] buffer, int offset, int limit) {
+ return parse(CharBuffer.wrap(buffer, offset, limit));
+ }
+
+ public String[] parse(CharSequence input) {
+ //prepend the text from the last reading to the text actually read
+ String lines = (trailingLine != null ? trailingLine : "");
+ lines += input.toString();
+ int tlLength = (trailingLine != null ? trailingLine.length() : 0);
+ int start = 0;
+ List ret = new ArrayList();
+ int length = input.length();
+ for (int i = 0; i < length; i++) { // going through the text read and searching for the new line
+ //we see '\n' or '\r', *not* '\r\n'
+ char c = input.charAt(i);
+ if (c == '\r'
+ && (i + 1 == length || input.charAt(i + 1) != '\n')
+ || c == '\n') {
+ String line = lines.substring(start, tlLength + i);
+ //move start to the character right after the new line
+ start = tlLength + (i + 1);
+ ret.add(line);
+ } else if (c == '\r'
+ && (i + 1 < length) && input.charAt(i + 1) == '\n') {//we see '\r\n'
+ String line = lines.substring(start, tlLength + i);
+ //skip the '\n' character
+ i += 1;
+ //move start to the character right after the new line
+ start = tlLength + (i + 1);
+ ret.add(line);
+ }
+ }
+ if (start < lines.length()) {
+ //new line was not found at the end of the input, the remaing text is stored for the next reading
+ trailingLine = lines.substring(start);
+ } else {
+ //null and not empty string to indicate that there is no valid input to write out;
+ //an empty string means that a new line character may be written out according
+ //to the LineProcessor implementation
+ trailingLine = null;
+ }
+ return ret.toArray(new String[ret.size()]);
+ }
+
+ public String getTrailingLine(boolean flush) {
+ String line = trailingLine;
+ if (flush) {
+ trailingLine = null;
+ }
+ return "".equals(line) ? null : line;
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/input/package-info.java b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/package-info.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/input/package-info.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation classes for stream processing API.
+ */
+package org.netbeans.modules.extexecution.base.input;
+
diff --git a/extexecution.base/src/org/netbeans/modules/extexecution/base/resources/Bundle.properties b/extexecution.base/src/org/netbeans/modules/extexecution/base/resources/Bundle.properties
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/modules/extexecution/base/resources/Bundle.properties
@@ -0,0 +1,46 @@
+# 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-2006 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.
+
+OpenIDE-Module-Name=External Execution Base API
+OpenIDE-Module-Display-Category=Base IDE
+OpenIDE-Module-Short-Description=Supports execution of external processes
+OpenIDE-Module-Long-Description=Supports execution of external processes.
diff --git a/extexecution.base/src/org/netbeans/spi/extexecution/base/EnvironmentFactory.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/EnvironmentFactory.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/EnvironmentFactory.java
@@ -0,0 +1,68 @@
+/*
+ * 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.base;
+
+import org.netbeans.api.extexecution.base.Environment;
+import org.netbeans.modules.extexecution.base.EnvironmentAccessor;
+
+/**
+ * The factory allowing SPI implementors of {@link EnvironmentImplementation}
+ * to create its API instances {@link Environment}.
+ *
+ * @author Petr Hejl
+ */
+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.base/src/org/netbeans/spi/extexecution/base/EnvironmentImplementation.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/EnvironmentImplementation.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/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.base;
+
+import java.util.Map;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.base.Environment;
+
+/**
+ * The interface representing the implementation
+ * of {@link Environment}.
+ *
+ * @see Environment
+ * @author Petr Hejl
+ */
+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.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderFactory.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderFactory.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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.base;
+
+import org.netbeans.modules.extexecution.base.ProcessBuilderAccessor;
+
+/**
+ * The factory allowing SPI implementors of {@link ProcessBuilderImplementation}
+ * to create its API instances {@link org.netbeans.api.extexecution.base.ProcessBuilder}.
+ *
+ * @author Petr Hejl
+ */
+public class ProcessBuilderFactory {
+
+ private ProcessBuilderFactory() {
+ super();
+ }
+
+ /**
+ * Creates the instance of {@link org.netbeans.api.extexecution.base.ProcessBuilder}
+ * from its SPI representation.
+ *
+ * @param impl SPI representation
+ * @param description human readable description of the builder
+ * @return the API instance
+ */
+ public static org.netbeans.api.extexecution.base.ProcessBuilder createProcessBuilder(
+ ProcessBuilderImplementation impl, String description) {
+ return ProcessBuilderAccessor.getDefault().createProcessBuilder(impl, description);
+ }
+}
diff --git a/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderImplementation.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderImplementation.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessBuilderImplementation.java
@@ -0,0 +1,92 @@
+/*
+ * 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.base;
+
+import java.io.IOException;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.base.Environment;
+import org.openide.util.Lookup;
+
+/**
+ * The interface representing the implementation
+ * of {@link org.netbeans.api.extexecution.base.ProcessBuilder}.
+ *
+ * @see org.netbeans.api.extexecution.base.ProcessBuilder
+ * @author Petr Hejl
+ */
+public interface ProcessBuilderImplementation 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.base.ProcessBuilder}
+ * by this as the content of the {@link Lookup} is included in
+ * {@link org.netbeans.api.extexecution.base.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 be 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.base/src/org/netbeans/spi/extexecution/base/ProcessParameters.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessParameters.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessParameters.java
@@ -0,0 +1,141 @@
+/*
+ * 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.base;
+
+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.base.ProcessParametersAccessor;
+
+/**
+ * The parameters configured for process creation.
+ *
+ * @see ProcessBuilderImplementation
+ * @author Petr Hejl
+ */
+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.base/src/org/netbeans/spi/extexecution/base/ProcessesImplementation.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessesImplementation.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/ProcessesImplementation.java
@@ -0,0 +1,74 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.spi.extexecution.base;
+
+import java.util.Map;
+
+/**
+ * The interface representing the implementation
+ * of {@link org.netbeans.api.extexecution.base.Processes}.
+ *
+ * Implementation of this interface should be published in default lookup
+ * in order to be used by
+ * {@link org.netbeans.api.extexecution.base.Processes}
+ *
+ * @see org.netbeans.api.extexecution.base.Processes
+ * @author Petr Hejl
+ */
+public interface ProcessesImplementation {
+
+ /**
+ * Kills the process passed as parameter and attempts to terminate
+ * all child processes in process tree.
+ *
+ * Any process running in environment containing the same variables
+ * with the same values as those passed in env (all of them)
+ * is supposed to be part of the process tree and may be killed.
+ *
+ * @param process process to kill
+ * @param environment map containing the variables and their values which the
+ * process must have to be considered being part of
+ * the tree to kill
+ */
+ void killTree(Process process, Map environment);
+}
diff --git a/extexecution.base/src/org/netbeans/spi/extexecution/base/package-info.java b/extexecution.base/src/org/netbeans/spi/extexecution/base/package-info.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/src/org/netbeans/spi/extexecution/base/package-info.java
@@ -0,0 +1,51 @@
+/*
+ * 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 support SPI for creation of external processes.
+ *
+ * @see org.netbeans.spi.extexecution.base.ProcessBuilderImplementation
+ */
+package org.netbeans.spi.extexecution.base;
+
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/BaseExecutionServiceTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/BaseExecutionServiceTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/BaseExecutionServiceTest.java
@@ -0,0 +1,513 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.Charset;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.InputProcessors;
+import org.netbeans.api.extexecution.base.input.TestInputUtils;
+import org.netbeans.api.extexecution.base.input.TestLineProcessor;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class BaseExecutionServiceTest extends NbTestCase {
+
+ private static final int PROCESS_TIMEOUT = 30000;
+
+ public BaseExecutionServiceTest(String name) {
+ super(name);
+ }
+
+ public void testSimpleRun() throws InterruptedException {
+ TestProcess process = new TestProcess(0);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor();
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+
+ process.destroy();
+ process.waitFor();
+ assertTrue(process.isFinished());
+ assertEquals(0, process.exitValue());
+ }
+
+ public void testReRun() throws InvocationTargetException, InterruptedException {
+ TestProcess process = new TestProcess(0);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor();
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ // first run
+ Future task = service.run();
+ assertNotNull(task);
+ assertFalse(process.isFinished());
+
+ process.waitStarted();
+ task.cancel(true);
+ assertTrue(task.isCancelled());
+
+ process.waitFor();
+ assertTrue(process.isFinished());
+ assertEquals(0, process.exitValue());
+
+ // second run
+ process = new TestProcess(1);
+ callable.addProcess(process);
+
+ task = service.run();
+ assertNotNull(task);
+ assertFalse(process.isFinished());
+
+ // we want to test real started process
+ process.waitStarted();
+ task.cancel(true);
+ assertTrue(task.isCancelled());
+
+ process.waitFor();
+ assertTrue(process.isFinished());
+ assertEquals(1, process.exitValue());
+ }
+
+ public void testCancelRerun() throws InterruptedException {
+ TestProcess process = new TestProcess(0);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor();
+ final CountDownLatch latch = new CountDownLatch(1);
+ descriptor = descriptor.preExecution(new Runnable() {
+ public void run() {
+ try {
+ latch.await();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ // first run
+ Future task = service.run();
+ assertNotNull(task);
+ assertFalse(process.isFinished());
+
+ task.cancel(true);
+ // guaranteed process was not executed
+ latch.countDown();
+
+ assertTrue(task.isCancelled());
+ assertFalse(process.isStarted());
+ assertFalse(process.isFinished());
+
+ // second run
+ task = service.run();
+ assertNotNull(task);
+ assertFalse(process.isFinished());
+
+ // we want to test real started process
+ process.waitStarted();
+ task.cancel(true);
+ assertTrue(task.isCancelled());
+
+ process.waitFor();
+ assertTrue(process.isFinished());
+ assertEquals(0, process.exitValue());
+ }
+
+ public void testConcurrentRun() throws InterruptedException, ExecutionException, BrokenBarrierException {
+ TestProcess process1 = new TestProcess(0);
+ TestProcess process2 = new TestProcess(1);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process1);
+ callable.addProcess(process2);
+
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor();
+ final CyclicBarrier barrier = new CyclicBarrier(3);
+ descriptor = descriptor.preExecution(new Runnable() {
+ public void run() {
+ try {
+ barrier.await();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ Future task1 = service.run();
+ Future task2 = service.run();
+
+ // wait for both tasks
+ barrier.await();
+
+ process1.destroy();
+ process2.destroy();
+
+ // TODO can we check returns values somehow ?
+ // task - process assignment is determined by the winner of the race :(
+ task1.get().intValue();
+ task2.get().intValue();
+
+ assertTrue(task1.isDone());
+ assertTrue(task2.isDone());
+
+ assertFalse(task1.isCancelled());
+ assertFalse(task2.isCancelled());
+ }
+
+ public void testHooks() throws InterruptedException, ExecutionException {
+ TestProcess process = new TestProcess(0);
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ class TestRunnable implements Runnable {
+
+ public volatile boolean executed;
+
+ public void run() {
+ executed = true;
+ }
+ }
+
+ class TestParametrizedRunnable implements ParametrizedRunnable {
+
+ public volatile boolean executed;
+
+ public void run(Integer parameter) {
+ executed = true;
+ }
+ }
+
+ TestRunnable preRunnable = new TestRunnable();
+ TestParametrizedRunnable postRunnable = new TestParametrizedRunnable();
+
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor();
+ descriptor = descriptor.preExecution(preRunnable).postExecution(postRunnable);
+
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ Future task = service.run();
+ assertNotNull(task);
+
+ process.waitStarted();
+ assertTrue(preRunnable.executed);
+
+ process.destroy();
+ assertEquals(0, task.get().intValue());
+ assertTrue(postRunnable.executed);
+ }
+
+ public void testCharset() throws InterruptedException, ExecutionException, TimeoutException {
+ Charset charset = Charset.forName("UTF-16LE");
+ 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, null);
+ is.setProcess(process);
+
+ TestCallable callable = new TestCallable();
+ callable.addProcess(process);
+
+ final TestLineProcessor processor = new TestLineProcessor(false);
+ BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor().charset(charset).outProcessorFactory(
+ new BaseExecutionDescriptor.InputProcessorFactory() {
+
+ public InputProcessor newInputProcessor() {
+ return InputProcessors.bridge(processor);
+ }
+ });
+
+ BaseExecutionService service = BaseExecutionService.newService(
+ callable, descriptor);
+
+ Future task = service.run();
+ assertNotNull(task);
+
+ assertEquals(0, task.get(PROCESS_TIMEOUT, TimeUnit.MILLISECONDS).intValue());
+ assertTrue(process.isFinished());
+ assertEquals(0, process.exitValue());
+
+ List processed = processor.getLinesProcessed();
+ assertEquals(lines.length, processed.size());
+ for (int i = 0; i < lines.length; i++) {
+ assertEquals(lines[i], processed.get(i));
+ }
+ }
+
+ private static class TestCallable implements Callable {
+
+ private final LinkedList processes = new LinkedList();
+
+ public TestCallable() {
+ super();
+ }
+
+ public synchronized void addProcess(TestProcess process) {
+ processes.add(process);
+ }
+
+ public synchronized Process call() throws Exception {
+ if (processes.isEmpty()) {
+ throw new IllegalStateException("No process configured");
+ }
+
+ TestProcess ret = processes.removeFirst();
+ ret.start();
+
+ return ret;
+ }
+ }
+
+ private static class TestProcess extends Process {
+
+ private final int returnValue;
+
+ private final InputStream is;
+
+ private final InputStream err;
+
+ private boolean finished;
+
+ private boolean started;
+
+ public TestProcess(int returnValue) {
+ this(returnValue, TestInputUtils.prepareInputStream(
+ new String[] {"Process line 1", "Process line 2", "Process line 3"}, "\n",
+ Charset.defaultCharset(), true), null);
+ }
+
+ public TestProcess(int returnValue, InputStream is, InputStream err) {
+ this.returnValue = returnValue;
+ this.is = is;
+ this.err = err;
+ }
+
+ public void start() {
+ synchronized (this) {
+ started = true;
+ notifyAll();
+ }
+ }
+
+ public boolean isStarted() {
+ synchronized (this) {
+ return started;
+ }
+ }
+
+ public boolean isFinished() {
+ synchronized (this) {
+ return finished;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ synchronized (this) {
+ if (finished) {
+ return;
+ }
+
+ finished = true;
+ notifyAll();
+ }
+ }
+
+ @Override
+ public int exitValue() {
+ synchronized (this) {
+ if (!finished) {
+ throw new IllegalStateException("Not finished yet");
+ }
+ }
+ return returnValue;
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ if (err != null) {
+ return err;
+ }
+ return new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return -1;
+ }
+ };
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return is;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ // throw it away
+ }
+ };
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ synchronized (this) {
+ while (!finished) {
+ wait();
+ }
+ }
+ return returnValue;
+ }
+
+ public void waitStarted() throws InterruptedException {
+ synchronized (this) {
+ while (!started) {
+ wait();
+ }
+ }
+ }
+ }
+
+ private static class TestInputStream extends FilterInputStream {
+
+ private Process process;
+
+ public TestInputStream(InputStream is) {
+ super(is);
+ }
+
+ public synchronized Process getProcess() {
+ return process;
+ }
+
+ public synchronized void setProcess(Process process) {
+ this.process = process;
+ }
+
+ @Override
+ public int available() throws IOException {
+ int available = super.available();
+ if (available <= 0) {
+ Process toDestroy = getProcess();
+ if (toDestroy != null) {
+ toDestroy.destroy();
+ }
+ }
+ return available;
+ }
+
+
+ @Override
+ public int read() throws IOException {
+ int val = super.read();
+ if (val < 0) {
+ Process toDestroy = getProcess();
+ if (toDestroy != null) {
+ toDestroy.destroy();
+ }
+ }
+ return val;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int val = super.read(b);
+ if (val < 0) {
+ Process toDestroy = getProcess();
+ if (toDestroy != null) {
+ toDestroy.destroy();
+ }
+ }
+ return val;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int val = super.read(b, off, len);
+ if (val < 0) {
+ Process toDestroy = getProcess();
+ if (toDestroy != null) {
+ toDestroy.destroy();
+ }
+ }
+ return val;
+ }
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessBuilderTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessBuilderTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessBuilderTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.api.extexecution.base;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.extexecution.base.EnvironmentFactory;
+import org.netbeans.spi.extexecution.base.EnvironmentImplementation;
+import org.netbeans.spi.extexecution.base.ProcessBuilderFactory;
+import org.netbeans.spi.extexecution.base.ProcessBuilderImplementation;
+import org.netbeans.spi.extexecution.base.ProcessParameters;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessBuilderTest extends NbTestCase {
+
+ public ProcessBuilderTest(String name) {
+ super(name);
+ }
+
+ public void testExecutable() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+
+ try {
+ builder.call();
+ fail("Empty executable does not throw exception");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+
+ builder.setExecutable("ls");
+ builder.call();
+ assertEquals("ls", testBuilder.getParameters().getExecutable());
+
+ builder.setExecutable("cd");
+ assertEquals("ls", testBuilder.getParameters().getExecutable());
+
+ builder.call();
+ assertEquals("cd", testBuilder.getParameters().getExecutable());
+ }
+
+ public void testWorkingDirectory() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.call();
+ assertNull(testBuilder.getParameters().getWorkingDirectory());
+
+ builder.setWorkingDirectory("test");
+ assertNull(testBuilder.getParameters().getWorkingDirectory());
+
+ builder.call();
+ assertEquals("test", testBuilder.getParameters().getWorkingDirectory());
+ }
+
+ public void testArguments() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.call();
+ assertTrue(testBuilder.getParameters().getArguments().isEmpty());
+
+ List arguments = new ArrayList();
+ Collections.addAll(arguments, "test1", "test2");
+ builder.setArguments(arguments);
+ assertTrue(testBuilder.getParameters().getArguments().isEmpty());
+
+ builder.call();
+ assertEquals(2, testBuilder.getParameters().getArguments().size());
+ assertEquals("test1", testBuilder.getParameters().getArguments().get(0));
+ assertEquals("test2", testBuilder.getParameters().getArguments().get(1));
+
+ arguments.remove(0);
+ assertEquals(2, testBuilder.getParameters().getArguments().size());
+ assertEquals("test1", testBuilder.getParameters().getArguments().get(0));
+ assertEquals("test2", testBuilder.getParameters().getArguments().get(1));
+
+ builder.call();
+ assertEquals(2, testBuilder.getParameters().getArguments().size());
+ assertEquals("test1", testBuilder.getParameters().getArguments().get(0));
+ assertEquals("test2", testBuilder.getParameters().getArguments().get(1));
+
+ builder.setArguments(arguments);
+ builder.call();
+ assertEquals(1, testBuilder.getParameters().getArguments().size());
+ assertEquals("test2", testBuilder.getParameters().getArguments().get(0));
+ }
+
+ public void testEnvironmentVariables() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.call();
+ assertTrue(testBuilder.getParameters().getEnvironmentVariables().isEmpty());
+
+ Environment environment = builder.getEnvironment();
+ environment.setVariable("key1", "value1");
+ environment.setVariable("key2", "value2");
+ assertTrue(testBuilder.getParameters().getEnvironmentVariables().isEmpty());
+
+ builder.call();
+ assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertEquals("value1", testBuilder.getParameters()
+ .getEnvironmentVariables().get("key1"));
+ assertEquals("value2", testBuilder.getParameters()
+ .getEnvironmentVariables().get("key2"));
+
+ environment.removeVariable("key1");
+ assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size());
+ assertEquals("value1", testBuilder.getParameters()
+ .getEnvironmentVariables().get("key1"));
+ assertEquals("value2", testBuilder.getParameters()
+ .getEnvironmentVariables().get("key2"));
+
+ builder.call();
+ assertEquals(1, testBuilder.getParameters()
+ .getEnvironmentVariables().size());
+ assertEquals("value2", testBuilder.getParameters()
+ .getEnvironmentVariables().get("key2"));
+ }
+
+ public void testEnvironment() 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");
+
+ 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 testRedirectErrorStream() throws IOException {
+ TestProcessBuilder testBuilder = new TestProcessBuilder();
+ ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder");
+ builder.setExecutable("ls");
+
+ builder.call();
+ assertFalse(testBuilder.getParameters().isRedirectErrorStream());
+
+ builder.setRedirectErrorStream(true);
+ assertFalse(testBuilder.getParameters().isRedirectErrorStream());
+
+ builder.call();
+ assertTrue(testBuilder.getParameters().isRedirectErrorStream());
+ }
+
+ private static class TestProcessBuilder implements ProcessBuilderImplementation {
+
+ 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.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessesTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessesTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/ProcessesTest.java
@@ -0,0 +1,152 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.extexecution.base.ProcessesImplementation;
+import org.openide.util.Lookup;
+import org.openide.util.test.MockLookup;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ProcessesTest extends NbTestCase {
+
+ public ProcessesTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ MockLookup.setInstances(new TestProcessesImplementation());
+ }
+
+ public void testKillTree() {
+ TestProcess process = new TestProcess();
+ Map env = new HashMap();
+ env.put("test1", "value1");
+ env.put("test2", "value2");
+
+ Processes.killTree(process, env);
+
+ ProcessesImplementation impl = Lookup.getDefault().lookup(ProcessesImplementation.class);
+ assertNotNull(impl);
+
+ TestProcessesImplementation testPerformer = (TestProcessesImplementation) impl;
+ assertEquals(process, testPerformer.getProcess());
+
+ Map perfEnv = testPerformer.getEnv();
+ assertEquals(2, perfEnv.size());
+
+ assertEquals(env.get("test1"), perfEnv.get("test1"));
+ assertEquals(env.get("test2"), perfEnv.get("test2"));
+ }
+
+ private static class TestProcess extends Process {
+
+ private boolean destroyed;
+
+ public boolean destroyCalled() {
+ return destroyed;
+ }
+
+ @Override
+ public void destroy() {
+ this.destroyed = true;
+ }
+
+ @Override
+ public int exitValue() {
+ return 0;
+ }
+
+ @Override
+ public InputStream getErrorStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public int waitFor() throws InterruptedException {
+ return 0;
+ }
+
+ }
+
+ private static class TestProcessesImplementation implements ProcessesImplementation {
+
+ private Process process;
+
+ private Map env;
+
+ @Override
+ public void killTree(Process process, Map environment) {
+ this.process = process;
+ this.env = environment;
+ }
+
+ public Process getProcess() {
+ return process;
+ }
+
+ public Map getEnv() {
+ return env;
+ }
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputProcessorsTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputProcessorsTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputProcessorsTest.java
@@ -0,0 +1,216 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class InputProcessorsTest extends NbTestCase {
+
+ private static final char[] PROXY_CHARS_CHUNK1 = "abcdefghij".toCharArray();
+
+ private static final char[] PROXY_CHARS_CHUNK2 = "jihgfedcba".toCharArray();
+
+ private static final char[][] PROXY_TEST_CHARS = new char[][] {
+ PROXY_CHARS_CHUNK1, PROXY_CHARS_CHUNK2
+ };
+
+ private static final List BRIDGE_TEST_LINES = new ArrayList();
+
+ private static final char[][] BRIDGE_TEST_CHARS;
+
+ static {
+ Collections.addAll(BRIDGE_TEST_LINES, "test1", "test2");
+
+ BRIDGE_TEST_CHARS = new char[BRIDGE_TEST_LINES.size()][];
+ for (int i = 0; i < BRIDGE_TEST_LINES.size(); i++) {
+ BRIDGE_TEST_CHARS[i] = (BRIDGE_TEST_LINES.get(i) + "\n").toCharArray();
+ }
+ }
+
+ public InputProcessorsTest(String name) {
+ super(name);
+ }
+
+ public void testBridge() throws IOException {
+ TestLineProcessor processor = new TestLineProcessor(false);
+ InputProcessor bridge = InputProcessors.bridge(processor);
+
+ for (char[] chunk : BRIDGE_TEST_CHARS) {
+ bridge.processInput(chunk);
+ }
+
+ assertEquals(0, processor.getResetCount());
+ assertEquals(BRIDGE_TEST_LINES, processor.getLinesProcessed());
+
+ bridge.reset();
+ assertEquals(1, processor.getResetCount());
+
+ bridge.close();
+ assertClosedConditions(bridge, true);
+ assertTrue(processor.isClosed());
+ }
+
+ public void testProxy() throws IOException {
+ TestInputProcessor processor1 = new TestInputProcessor(false);
+ TestInputProcessor processor2 = new TestInputProcessor(false);
+
+ InputProcessor proxy = InputProcessors.proxy(processor1, processor2);
+ int size = 0;
+ for (char[] chunk : PROXY_TEST_CHARS) {
+ proxy.processInput(chunk);
+ size += chunk.length;
+ }
+
+ char[] expected = new char[size];
+ int position = 0;
+ for (char[] chunk : PROXY_TEST_CHARS) {
+ System.arraycopy(chunk, 0, expected, position, chunk.length);
+ position += chunk.length;
+ }
+
+ assertEquals(0, processor1.getResetCount());
+ assertEquals(0, processor2.getResetCount());
+
+ assertTrue(Arrays.equals(expected, processor1.getCharsProcessed()));
+ assertTrue(Arrays.equals(expected, processor2.getCharsProcessed()));
+
+ proxy.reset();
+
+ assertEquals(1, processor1.getResetCount());
+ assertEquals(1, processor2.getResetCount());
+
+ proxy.close();
+ assertClosedConditions(proxy, true);
+
+ assertTrue(processor1.isClosed());
+ assertTrue(processor2.isClosed());
+ }
+
+ public void testPrinting() throws IOException {
+ TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
+ InputProcessor processor = InputProcessors.printing(writer);
+
+ processor.processInput("pre".toCharArray());
+ assertEquals("pre", writer.getPrintedRaw());
+ processor.processInput("test1\n".toCharArray());
+ assertEquals("pretest1\n", writer.getPrintedRaw());
+ processor.processInput("test2\n".toCharArray());
+ assertEquals("pretest1\ntest2\n", writer.getPrintedRaw());
+ processor.processInput("test3".toCharArray());
+ assertEquals("pretest1\ntest2\ntest3", writer.getPrintedRaw());
+
+ processor.processInput("\n".toCharArray());
+
+ processor.close();
+ assertClosedConditions(processor, false);
+ }
+
+ public void testPrintingCloseOrdering() throws IOException {
+ final TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
+ final InputProcessor delegate = InputProcessors.printing(writer);
+
+ InputProcessor processor = new InputProcessor() {
+
+ public void processInput(char[] chars) throws IOException {
+ delegate.processInput(chars);
+ }
+
+ public void reset() throws IOException {
+ delegate.reset();
+ }
+
+ public void close() throws IOException {
+ delegate.processInput("closing mark".toCharArray());
+ delegate.close();
+ }
+ };
+
+
+ processor.processInput("first".toCharArray());
+ assertEquals("first", writer.getPrintedRaw());
+ processor.processInput("second\n".toCharArray());
+ assertEquals("firstsecond\n", writer.getPrintedRaw());
+
+ processor.close();
+ assertEquals("firstsecond\nclosing mark", writer.getPrintedRaw());
+ assertClosedConditions(processor, false);
+ }
+
+ private static void assertEquals(List expected, List value) {
+ assertEquals(expected.size(), value.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), value.get(i));
+ }
+ }
+
+ private static void assertClosedConditions(InputProcessor inputProcessor,
+ boolean reset) throws IOException {
+
+ try {
+ inputProcessor.processInput(new char[] {'0'});
+ fail("Does not throw IllegalStateException after close");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+
+ if (reset) {
+ try {
+ inputProcessor.reset();
+ fail("Does not throw IllegalStateException after close");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersFileTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersFileTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersFileTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import org.netbeans.api.extexecution.base.input.InputReaders.FileInput;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class InputReadersFileTest extends NbTestCase {
+
+ private static final char[] TEST_CHARS = "abcdefghij".toCharArray();
+
+ private static final char[] TEST_CHARS_ROTATE = "jihgfedcba".toCharArray();
+
+ private static final Charset TEST_CHARSET = Charset.forName("UTF-8");
+
+ private static final int MAX_RETRIES = TEST_CHARS.length * 2;
+
+ private File byteFile;
+
+ private File byteFileRotate;
+
+ public InputReadersFileTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ byteFile = TestInputUtils.prepareFile(
+ "testFile.txt", getWorkDir(), TEST_CHARS, TEST_CHARSET);
+ byteFileRotate = TestInputUtils.prepareFile(
+ "testFileRotate.txt", getWorkDir(), TEST_CHARS_ROTATE, TEST_CHARSET);
+ }
+
+ public void testReadInput() throws IOException {
+ final FileInput fileInput = new FileInput(byteFile, TEST_CHARSET);
+ InputReader reader = InputReaders.forFileInputProvider(new InputReaders.FileInput.Provider() {
+
+ public FileInput getFileInput() {
+ return fileInput;
+ }
+ });
+ TestInputProcessor processor = new TestInputProcessor(false);
+
+ int read = 0;
+ int retries = 0;
+ while (read < TEST_CHARS.length && retries < MAX_RETRIES) {
+ read += reader.readInput(processor);
+ retries++;
+ }
+
+ assertEquals(read, TEST_CHARS.length);
+ assertEquals(0, processor.getResetCount());
+
+ assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed()));
+ }
+
+ public void testRotation() throws IOException {
+ TestProvider provider = new TestProvider(byteFile, TEST_CHARSET);
+
+ InputReader outputReader = InputReaders.forFileInputProvider(provider);
+ TestInputProcessor processor = new TestInputProcessor(true);
+
+ int read = 0;
+ int retries = 0;
+ while (read < TEST_CHARS.length && retries < MAX_RETRIES) {
+ read += outputReader.readInput(processor);
+ retries++;
+ }
+
+ assertEquals(read, TEST_CHARS.length);
+ assertEquals(0, processor.getResetCount());
+
+ assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed()));
+
+ // file rotation
+ provider.setFile(byteFileRotate);
+
+ read = 0;
+ retries = 0;
+ while (read < TEST_CHARS_ROTATE.length && retries < MAX_RETRIES) {
+ read += outputReader.readInput(processor);
+ retries++;
+ }
+
+ assertEquals(read, TEST_CHARS_ROTATE.length);
+ assertEquals(1, processor.getResetCount());
+
+ assertTrue(Arrays.equals(TEST_CHARS_ROTATE, processor.getCharsProcessed()));
+ }
+
+ public void testFactory() {
+ try {
+ InputReaders.forFile(null, null);
+ fail("Accepts null file generator"); // NOI18N
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testClose() throws IOException {
+ final FileInput fileInput = new FileInput(byteFile, TEST_CHARSET);
+ InputReader reader = InputReaders.forFileInputProvider(new InputReaders.FileInput.Provider() {
+
+ public FileInput getFileInput() {
+ return fileInput;
+ }
+ });
+ reader.close();
+
+ try {
+ reader.readInput(null);
+ fail("Reader not throw exception on read after closing it"); // NOI18N
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ private static class TestProvider implements InputReaders.FileInput.Provider {
+
+ private final Charset charset;
+
+ private FileInput fileInput;
+
+ public TestProvider(File file, Charset charset) {
+ this.charset = charset;
+ setFile(file);
+ }
+
+ public final FileInput getFileInput() {
+ return fileInput;
+ }
+
+ public final void setFile(File file) {
+ this.fileInput = new FileInput(file, charset);
+ }
+
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersReaderTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersReaderTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersReaderTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+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;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class InputReadersReaderTest extends NbTestCase {
+
+ private static final char[] TEST_CHARS = "abcdefghij".toCharArray();
+
+ private static final int MAX_RETRIES = TEST_CHARS.length * 2;
+
+ private static final Charset TEST_CHARSET = Charset.forName("UTF-8");
+
+ public InputReadersReaderTest(String name) {
+ super(name);
+ }
+
+ public void testReadInput() throws IOException {
+ Reader reader = new InputStreamReader(TestInputUtils.prepareInputStream(
+ TEST_CHARS, TEST_CHARSET), TEST_CHARSET);
+ 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()));
+ }
+
+ 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.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersStreamTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersStreamTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/InputReadersStreamTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class InputReadersStreamTest extends NbTestCase {
+
+ private static final char[] TEST_CHARS = "abcdefghij".toCharArray();
+
+ private static final int MAX_RETRIES = TEST_CHARS.length * 2;
+
+ private static final Charset TEST_CHARSET = Charset.forName("UTF-8");
+
+ public InputReadersStreamTest(String name) {
+ super(name);
+ }
+
+ public void testReadInput() throws IOException {
+ InputReader reader = InputReaders.forStream(TestInputUtils.prepareInputStream(
+ TEST_CHARS, TEST_CHARSET), TEST_CHARSET);
+ TestInputProcessor processor = new TestInputProcessor(false);
+
+ int read = 0;
+ int retries = 0;
+ while (read < TEST_CHARS.length && retries < MAX_RETRIES) {
+ read += reader.readInput(processor);
+ retries++;
+ }
+
+ assertEquals(read, TEST_CHARS.length);
+ assertEquals(0, processor.getResetCount());
+
+ assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed()));
+ }
+
+ public void testFactory() {
+ try {
+ InputReaders.forStream(null, TEST_CHARSET);
+ fail("Accepts null stream"); // NOI18N
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
+ public void testClose() throws IOException {
+ InputReader reader = InputReaders.forStream(TestInputUtils.prepareInputStream(
+ TEST_CHARS, TEST_CHARSET), TEST_CHARSET);
+ reader.close();
+
+ try {
+ reader.readInput(null);
+ fail("Reader not throw exception on read after closing it"); // NOI18N
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/LineProcessorsTest.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/LineProcessorsTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/LineProcessorsTest.java
@@ -0,0 +1,335 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.junit.RandomlyFails;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class LineProcessorsTest extends NbTestCase {
+
+ private static final String WAIT_RELEASE_STRING = "test"; // NOI18N
+
+ private static final long DEADLOCK_TIMEOUT = 1000;
+
+ private static final int WAIT_THREAD_COUNT = 5;
+
+ private static final int PRODUCER_THREAD_COUNT = 5;
+
+ private static final long TEST_TIMEOUT = 5000;
+
+ private static final List PROXY_TEST_LINES = new ArrayList();
+
+ private static final List PRINTING_TEST_LINES = new ArrayList(5);
+
+ static {
+ Collections.addAll(PROXY_TEST_LINES, "test1", "test2");
+
+ Collections.addAll(PRINTING_TEST_LINES,
+ "the first test line",
+ "the second test line",
+ "the third test line",
+ "the fourth test line",
+ "the fifth test line");
+ }
+
+ private ExecutorService executor;
+
+ public LineProcessorsTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ executor = Executors.newCachedThreadPool();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ executor.shutdownNow();
+ }
+
+ public void testProxy() {
+ TestLineProcessor processor1 = new TestLineProcessor(false);
+ TestLineProcessor processor2 = new TestLineProcessor(false);
+
+ LineProcessor proxy = LineProcessors.proxy(processor1, processor2);
+ for (String line : PROXY_TEST_LINES) {
+ proxy.processLine(line);
+ }
+
+ assertEquals(0, processor1.getResetCount());
+ assertEquals(0, processor2.getResetCount());
+
+ assertEquals(PROXY_TEST_LINES, processor1.getLinesProcessed());
+ assertEquals(PROXY_TEST_LINES, processor2.getLinesProcessed());
+
+ proxy.reset();
+
+ assertEquals(1, processor1.getResetCount());
+ assertEquals(1, processor2.getResetCount());
+
+ proxy.close();
+ assertClosedConditions(proxy);
+
+ assertTrue(processor1.isClosed());
+ assertTrue(processor2.isClosed());
+ }
+
+ public void testPrinting() {
+ TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
+ LineProcessor lineProcessor = LineProcessors.printing(writer);
+ for (String line : PRINTING_TEST_LINES) {
+ lineProcessor.processLine(line);
+ }
+ assertEquals(PRINTING_TEST_LINES, writer.getPrinted());
+
+ lineProcessor.close();
+ assertClosedConditions(lineProcessor);
+ }
+
+ public void testPrintingCloseOrdering() {
+ final TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
+ final LineProcessor delegate = LineProcessors.printing(writer);
+
+ LineProcessor lineProcessor = new LineProcessor() {
+
+ public void processLine(String line) {
+ delegate.processLine(line);
+ }
+
+ public void reset() {
+ delegate.reset();
+ }
+
+ public void close() {
+ delegate.processLine("closing mark");
+ delegate.close();
+ }
+ };
+
+ for (String line : PRINTING_TEST_LINES) {
+ lineProcessor.processLine(line);
+ }
+ assertEquals(PRINTING_TEST_LINES, writer.getPrinted());
+
+ lineProcessor.close();
+ List printed = new ArrayList(PRINTING_TEST_LINES);
+ printed.add("closing mark");
+ assertEquals(printed, writer.getPrinted());
+ assertClosedConditions(lineProcessor);
+ }
+
+ public void testWaiting() throws InterruptedException, BrokenBarrierException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final LineProcessor lineProcessor = LineProcessors.patternWaiting(
+ Pattern.compile(WAIT_RELEASE_STRING), latch);
+ CyclicBarrier barrier = new CyclicBarrier(2);
+
+ executor.execute(new WaitRunnable(latch, barrier));
+ barrier.await();
+ lineProcessor.processLine(WAIT_RELEASE_STRING);
+
+ try {
+ barrier.await(DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException ex) {
+ fail("Deadlock occurs");
+ }
+
+ executor.execute(new WaitRunnable(latch, barrier));
+ barrier.await();
+ try {
+ barrier.await(DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException ex) {
+ fail("Deadlock occurs");
+ }
+
+ lineProcessor.close();
+ assertClosedConditions(lineProcessor);
+ }
+
+ @RandomlyFails // NB-Core-Build #8029
+ public void testWaitingThreadSafety() throws InterruptedException, BrokenBarrierException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final LineProcessor lineProcessor = LineProcessors.patternWaiting(
+ Pattern.compile(WAIT_RELEASE_STRING), latch);
+ CyclicBarrier barrier = new CyclicBarrier(WAIT_THREAD_COUNT + 1);
+
+ for (int i = 0; i < WAIT_THREAD_COUNT; i++) {
+ executor.execute(new WaitRunnable(latch, barrier));
+ }
+
+ barrier.await();
+
+ Random random = new Random();
+ for (int i = 0; i < PRODUCER_THREAD_COUNT; i++) {
+ executor.execute(new ProducerRunnable(lineProcessor, WAIT_RELEASE_STRING, random.nextInt(5)));
+ }
+
+ // guarantee finish
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ Thread.sleep(TEST_TIMEOUT);
+ lineProcessor.processLine(WAIT_RELEASE_STRING);
+ } catch (InterruptedException ex) {
+ //throw new RuntimeException(ex);
+ }
+ }
+ });
+
+ try {
+ barrier.await(TEST_TIMEOUT + DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException ex) {
+ fail("Deadlock occurs");
+ }
+ }
+
+ private static void assertEquals(List expected, List value) {
+ assertEquals(expected.size(), value.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i), value.get(i));
+ }
+ }
+
+ private static void assertClosedConditions(LineProcessor lineProcessor) {
+ try {
+ lineProcessor.processLine("something");
+ fail("Does not throw IllegalStateException after close");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+
+ try {
+ lineProcessor.reset();
+ fail("Does not throw IllegalStateException after close");
+ } catch (IllegalStateException ex) {
+ // expected
+ }
+ }
+
+ private static class WaitRunnable implements Runnable {
+
+ private final CountDownLatch latch;
+
+ private final CyclicBarrier barrier;
+
+ public WaitRunnable(CountDownLatch latch, CyclicBarrier barrier) {
+ this.latch = latch;
+ this.barrier = barrier;
+ }
+
+ public void run() {
+ try {
+ barrier.await();
+ latch.await();
+ barrier.await();
+ } catch (InterruptedException ex) {
+ // timeouted test
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException ex) {
+ // timeouted test
+ }
+ }
+
+ }
+
+ private static class ProducerRunnable implements Runnable {
+
+ private final LineProcessor lineProcessor;
+
+ private final String releaseString;
+
+ private final Random random = new Random();
+
+ private final int iterations;
+
+ public ProducerRunnable(LineProcessor lineProcessor, String releaseString, int iterations) {
+ this.lineProcessor = lineProcessor;
+ this.releaseString = releaseString;
+ this.iterations = iterations;
+ }
+
+ public void run() {
+ for (int i = 0; i < iterations; i++) {
+ if (Thread.interrupted()) {
+ return;
+ }
+
+ int val = random.nextInt(10);
+ if (val == 0) {
+ lineProcessor.processLine(releaseString);
+ return;
+ } else {
+ lineProcessor.processLine("generated " + val);
+ }
+
+ try {
+ Thread.sleep(random.nextInt(300));
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputProcessor.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputProcessor.java
@@ -0,0 +1,91 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+/**
+ *
+ * This class is NotThreadSafe.
+ * @author Petr Hejl
+ */
+public class TestInputProcessor implements InputProcessor {
+
+ private final boolean cleanBytesOnReset;
+
+ private StringBuilder charsProcessed = new StringBuilder();
+
+ private int resetCount = 0;
+
+ private boolean closed;
+
+ public TestInputProcessor(boolean cleanBytesOnReset) {
+ this.cleanBytesOnReset = cleanBytesOnReset;
+ }
+
+ public void processInput(char[] chars) {
+ charsProcessed.append(chars);
+ }
+
+ public void reset() {
+ resetCount++;
+ if (cleanBytesOnReset) {
+ charsProcessed.setLength(0);
+ }
+ }
+
+ public void close() {
+ closed = true;
+ }
+
+ public char[] getCharsProcessed() {
+ return charsProcessed.toString().toCharArray();
+ }
+
+ public int getResetCount() {
+ return resetCount;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputUtils.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputUtils.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputUtils.java
@@ -0,0 +1,148 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public final class TestInputUtils {
+
+ private TestInputUtils() {
+ super();
+ }
+
+ public static InputStream prepareInputStream(String[] lines, String separator,
+ Charset charset, boolean terminate) {
+
+ StringBuffer buffer = new StringBuffer();
+ for (int i = 0; i < lines.length; i++) {
+ buffer.append(lines[i]);
+ if (terminate || i < (lines.length - 1)) {
+ buffer.append(separator);
+ }
+ }
+
+ ByteBuffer byteBuffer = charset.encode(buffer.toString());
+ int length = byteBuffer.limit();
+ byte[] byteArray = new byte[length];
+ byteBuffer.position(0);
+ byteBuffer.get(byteArray);
+
+ return prepareInputStream(byteArray);
+ }
+
+ public static InputStream prepareInputStream(char[] chars, Charset charset) {
+ CharBuffer wrapped = CharBuffer.wrap(chars);
+ ByteBuffer buffer = charset.encode(wrapped);
+ byte[] bytes = new byte[buffer.limit()];
+ buffer.get(bytes);
+ return prepareInputStream(bytes);
+ }
+
+ private static InputStream prepareInputStream(byte[] bytes) {
+ return new ByteArrayInputStream(bytes.clone());
+ }
+
+ public static File prepareFile(String name, File workDir,
+ String[] lines, String separator, Charset charset, boolean terminate) throws IOException {
+
+ File file = new File(workDir, name);
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset));
+ try {
+ for (int i = 0; i < lines.length; i++) {
+ writer.write(lines[i]);
+ if (terminate || i < (lines.length - 1)) {
+ writer.write(separator);
+ }
+ }
+ } finally {
+ writer.close();
+ }
+ return file;
+ }
+
+ public static File prepareFile(String name, File workDir, char[] chars,
+ Charset charset) throws IOException {
+
+ File file = new File(workDir, name);
+ Writer writer = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(file)), charset);
+ try {
+ writer.write(chars);
+ } finally {
+ writer.close();
+ }
+ return file;
+ }
+
+ public static class EndlessAsciiInputStream extends InputStream {
+
+ private final Random random = new Random();
+
+ @Override
+ public int read() throws IOException {
+ return random.nextInt(256);
+ }
+
+ @Override
+ public int available() throws IOException {
+ return 1;
+ }
+
+ }
+
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputWriter.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputWriter.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestInputWriter.java
@@ -0,0 +1,95 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class TestInputWriter extends PrintWriter {
+
+ private List printed = new ArrayList();
+
+ private StringBuilder builder = new StringBuilder();
+
+ private int resetsProcessed;
+
+ private String cache = "";
+
+ public TestInputWriter(Writer w) {
+ super(w);
+ }
+
+ @Override
+ public void print(String s) {
+ cache = s;
+ builder.append(s);
+ super.print(s);
+ }
+
+ @Override
+ public void println() {
+ printed.add(cache);
+ builder.append("\n");
+ cache = "";
+ super.println();
+ }
+
+ public List getPrinted() {
+ return Collections.unmodifiableList(printed);
+ }
+
+ public String getPrintedRaw() {
+ return builder.toString();
+ }
+
+ public int getResetsProcessed() {
+ return resetsProcessed;
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestLineProcessor.java b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestLineProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/api/extexecution/base/input/TestLineProcessor.java
@@ -0,0 +1,94 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.base.input;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * This class is NotThreadSafe.
+ * @author Petr Hejl
+ */
+public class TestLineProcessor implements LineProcessor {
+
+ private final boolean clearLinesOnReset;
+
+ private List linesProcessed = new ArrayList();
+
+ private int resetCount = 0;
+
+ private boolean closed;
+
+ public TestLineProcessor(boolean clearLinesOnReset) {
+ this.clearLinesOnReset = clearLinesOnReset;
+ }
+
+ public void processLine(String line) {
+ linesProcessed.add(line);
+ }
+
+ public void reset() {
+ resetCount++;
+ if (clearLinesOnReset) {
+ linesProcessed.clear();
+ }
+ }
+
+ public void close() {
+ closed = true;
+ }
+
+ public List getLinesProcessed() {
+ return Collections.unmodifiableList(linesProcessed);
+ }
+
+ public int getResetCount() {
+ return resetCount;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilderTest.java b/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilderTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/ExternalProcessBuilderTest.java
@@ -0,0 +1,112 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.base;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class ExternalProcessBuilderTest extends NbTestCase {
+
+ public ExternalProcessBuilderTest(String name) {
+ super(name);
+ }
+
+ public void testEnvironment() {
+ ExternalProcessBuilder creator = new ExternalProcessBuilder("command");
+ creator = creator.addEnvironmentVariable("test1", "value1");
+ creator = creator.addEnvironmentVariable("test2", "value2");
+
+ Map env = new HashMap(
+ creator.buildEnvironment(Collections.emptyMap()));
+ assertEquals("value1", env.remove("test1"));
+ assertEquals("value2", env.remove("test2"));
+ assertTrue(env.isEmpty());
+ }
+
+ public void testPath() {
+ ExternalProcessBuilder creator = new ExternalProcessBuilder("command");
+ Map original = new HashMap();
+ original.put("PATH", "original");
+
+ // original path
+ Map env = new HashMap(
+ creator.buildEnvironment(original));
+ assertEquals("original", env.remove("PATH"));
+ assertTrue(env.isEmpty());
+
+ // some added path
+ File addedPath = new File("addedPath");
+ creator = creator.prependPath(addedPath);
+ env = new HashMap(creator.buildEnvironment(original));
+ assertEquals(addedPath.getAbsolutePath().replace(" ", "\\ ") + File.pathSeparator + "original", env.remove("PATH"));
+ assertTrue(env.isEmpty());
+
+ // yet another path
+ File nextPath = new File("nextPath");
+ creator = creator.prependPath(nextPath);
+ env = new HashMap(creator.buildEnvironment(original));
+ assertEquals(
+ nextPath.getAbsolutePath().replace(" ", "\\ ") + File.pathSeparator
+ + addedPath.getAbsolutePath().replace(" ", "\\ ") + File.pathSeparator
+ + "original", env.remove("PATH"));
+ assertTrue(env.isEmpty());
+ }
+
+ public void testImmutability() throws IOException {
+ ExternalProcessBuilder builder = new ExternalProcessBuilder("ls");
+
+ assertNotSame(builder, builder.addArgument("test"));
+ assertNotSame(builder, builder.addEnvironmentVariable("test", "test"));
+ assertNotSame(builder, builder.prependPath(getWorkDir()));
+ assertNotSame(builder, builder.redirectErrorStream(true));
+ assertNotSame(builder, builder.workingDirectory(getWorkDir()));
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/input/LineParsingHelperTest.java b/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/input/LineParsingHelperTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/modules/extexecution/base/input/LineParsingHelperTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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]"
+ *
+ * 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 2007 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.base.input;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class LineParsingHelperTest extends NbTestCase {
+
+ private static final String[] TEST_LINES = new String[] {"line1", "line2", "line3"}; // NOI18N
+
+ private static final String[] TEST_SEPARATORS = new String[] {"\n", "\r", "\r\n"}; // NOI18N
+
+ private static final int EXTENDED_LENGTH = 10;
+
+ public LineParsingHelperTest(String name) {
+ super(name);
+ }
+
+ public void testParsingCharacterIterator() {
+ for (String separator : TEST_SEPARATORS) {
+ StringBuffer testInput = new StringBuffer();
+ for (String line : TEST_LINES) {
+ testInput.append(line).append(separator);
+ }
+
+ LineParsingHelper helper = new LineParsingHelper();
+ String[] lines = helper.parse(testInput);
+ checkParsingResults(lines, helper);
+ }
+ }
+
+ public void testParsingCharacterArray() {
+ for (String separator : TEST_SEPARATORS) {
+ StringBuffer testInput = new StringBuffer();
+ for (String line : TEST_LINES) {
+ testInput.append(line).append(separator);
+ }
+
+ LineParsingHelper helper = new LineParsingHelper();
+ char[] characterTestInput = new char[testInput.length()];
+ testInput.getChars(0, testInput.length(), characterTestInput, 0);
+ String[] lines = helper.parse(characterTestInput);
+ checkParsingResults(lines, helper);
+
+ characterTestInput = new char[testInput.length() + EXTENDED_LENGTH];
+ testInput.getChars(0, testInput.length(), characterTestInput, 0);
+ lines = helper.parse(characterTestInput, 0, testInput.length());
+ checkParsingResults(lines, helper);
+ }
+ }
+
+ public void testTrailingLine() {
+ String testLine = "line1\nline2\nline3"; // NOI18N
+ LineParsingHelper helper = new LineParsingHelper();
+ String[] lines = helper.parse(testLine);
+
+ assertEquals(2, lines.length);
+ assertEquals("line1", lines[0]); // NOI18N
+ assertEquals("line2", lines[1]); // NOI18N
+
+ assertEquals("line3", helper.getTrailingLine(false));
+ assertEquals("line3", helper.getTrailingLine(true));
+ assertEquals(null, helper.getTrailingLine(true));
+
+ testLine = "line1\nline2\nline3\n";
+ helper.parse(testLine);
+ assertEquals(null, helper.getTrailingLine(true));
+ }
+
+ private void checkParsingResults(String[] lines, LineParsingHelper helper) {
+ assertEquals(TEST_LINES.length, lines.length);
+
+ for (int i = 0; i < TEST_LINES.length; i++) {
+ assertEquals(TEST_LINES[i], lines[i]);
+ }
+
+ assertEquals(null, helper.getTrailingLine(false));
+ assertEquals(null, helper.getTrailingLine(true));
+ }
+}
diff --git a/extexecution.base/test/unit/src/org/netbeans/spi/extexecution/base/ProcessParametersTest.java b/extexecution.base/test/unit/src/org/netbeans/spi/extexecution/base/ProcessParametersTest.java
new file mode 100644
--- /dev/null
+++ b/extexecution.base/test/unit/src/org/netbeans/spi/extexecution/base/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.base;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.extexecution.base.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"));
+ }
+}
diff --git a/extexecution.impl/nbproject/project.xml b/extexecution.impl/nbproject/project.xml
--- a/extexecution.impl/nbproject/project.xml
+++ b/extexecution.impl/nbproject/project.xml
@@ -30,6 +30,15 @@
+ org.netbeans.modules.extexecution.base
+
+
+
+ 2
+ 1.0
+
+
+ org.netbeans.modules.options.api
diff --git a/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessTreeDestroyPerformer.java b/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessTreeDestroyPerformer.java
deleted file mode 100644
--- a/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessTreeDestroyPerformer.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 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]"
- *
- * 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 2009 Sun Microsystems, Inc.
- */
-
-package org.netbeans.modules.extexecution.destroy;
-
-import java.util.Map;
-import org.netbeans.processtreekiller.ProcessTreeKiller;
-import org.netbeans.spi.extexecution.destroy.ProcessDestroyPerformer;
-import org.openide.util.lookup.ServiceProvider;
-
-/**
- *
- * @author mkleint
- */
-@ServiceProvider(service=ProcessDestroyPerformer.class)
-public class ProcessTreeDestroyPerformer implements ProcessDestroyPerformer {
-
- @Override
- public void destroy(Process process, Map env) {
- ProcessTreeKiller.get().kill(process, env);
- }
-
-}
diff --git a/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessesImpl.java b/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessesImpl.java
new file mode 100644
--- /dev/null
+++ b/extexecution.impl/src/org/netbeans/modules/extexecution/destroy/ProcessesImpl.java
@@ -0,0 +1,72 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.destroy;
+
+import java.util.Map;
+import org.netbeans.processtreekiller.ProcessTreeKiller;
+import org.netbeans.spi.extexecution.base.ProcessesImplementation;
+import org.netbeans.spi.extexecution.destroy.ProcessDestroyPerformer;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+/**
+ *
+ * @author mkleint
+ */
+@ServiceProviders({
+ @ServiceProvider(service=ProcessDestroyPerformer.class),
+ @ServiceProvider(service=ProcessesImplementation.class)
+})
+public class ProcessesImpl implements ProcessDestroyPerformer, ProcessesImplementation {
+
+ @Override
+ public void destroy(Process process, Map env) {
+ ProcessTreeKiller.get().kill(process, env);
+ }
+
+ @Override
+ public void killTree(Process process, Map environment) {
+ ProcessTreeKiller.get().kill(process, environment);
+ }
+
+}
diff --git a/extexecution/apichanges.xml b/extexecution/apichanges.xml
--- a/extexecution/apichanges.xml
+++ b/extexecution/apichanges.xml
@@ -117,6 +117,25 @@
+ API split and deprecation
+
+
+
+
+
+ Splitting the API deprecating major parts.
+
+
+
+
+
+
+
+
+
+
+
+ Advice to throw UserQuestionException
diff --git a/extexecution/arch.xml b/extexecution/arch.xml
--- a/extexecution/arch.xml
+++ b/extexecution/arch.xml
@@ -47,33 +47,47 @@
-->
+ The major parts of this API has been refactored to External Execution Base API
+ in version 1.43. This API is now to be used in situations where the base
+ support is not sufficient such as when you need progress and output window
+ integration.
+
+
The External Execution module provides the
that contains support for execution of external processes in the IDE. It also
provide support class for the actual creation of the external process
- and support for destroying the process tree. There is also abstraction of
- process builder.
+ and support for destroying the process tree.
+ There is also abstraction of
+ process builder. The builder is now deprecated and replaced by one in
+ External Execution Base API.
- Another exported API
+ Another exported API
define interfaces for input processing (character or line based)
- and provides common implementations of these with factory methods.
+ and provides common implementations of these with factory methods.
+ This API is now deprecated in favor of External Execution Base API.
Natural extension to input processing API is printing API
that defines interfaces transforming lines to lines printed to
- org.openide.windows.OutputWriter. API provides common implementations too.
+ org.openide.windows.OutputWriter. API provides common implementations too
+ and provides processor for org.openide.windows.OutputWriter printing.
- The SPI
+ The SPI
- allows different implementations of process builder.
+ allows different implementations of process builder.
+ This API is now deprecated in favor of External Execution Base API.
- There is also SPI allowing to register support for destroying the process tree
- .
+ There is also SPI allowing to
+ register support for destroying the process tree
+ .
+ This API is now deprecated in favor of org.netbeans.spi.extexecution.base.ProcessesImplementation
+ from External Execution Base API.
The
@@ -175,8 +189,9 @@
- Client needs to process character data coming from stream, file or other
- source.
+ Client needs to process character
+ data coming from stream, file or other source. This usecase should
+ be solved by External Execution Base API.
- Third party wants to implement custom process builder to provide
- additional functionality, such as remote execution.
+ Third party wants to implement custom process builder to provide
+ additional functionality, such as remote execution.
+ This usecase should be solved by External Execution Base API.
In order to do so it will implement
@@ -242,7 +258,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.
+ Implementations of FileOpenHandler
+ and HttpOpenHandler
+ are looked up to provide support for file and url opening by default line
+ convertors created by LineConvertors.
+
+
+ Implementation of OptionOpenHandler
+ is looked up to provide support for options opening from output window
+ when configured by ExecutionDescriptor.
+
* Thread safety of this class depends on type of objects passed to its
@@ -66,6 +68,8 @@
*/
public final class ExecutionDescriptor {
+ private static final Logger LOGGER = Logger.getLogger(ExecutionDescriptor.class.getName());
+
// TODO provide constants for common descriptors (are there any?)
private final Runnable preExecution;
@@ -95,8 +99,12 @@
private final LineConvertorFactory errConvertorFactory;
private final InputProcessorFactory outProcessorFactory;
+
+ private final InputProcessorFactory2 outProcessorFactory2;
private final InputProcessorFactory errProcessorFactory;
+
+ private final InputProcessorFactory2 errProcessorFactory2;
private final InputOutput inputOutput;
@@ -128,7 +136,9 @@
this.outConvertorFactory = data.outConvertorFactory;
this.errConvertorFactory = data.errConvertorFactory;
this.outProcessorFactory = data.outProcessorFactory;
+ this.outProcessorFactory2 = data.outProcessorFactory2;
this.errProcessorFactory = data.errProcessorFactory;
+ this.errProcessorFactory2 = data.errProcessorFactory2;
this.inputOutput = data.inputOutput;
this.rerunCondition = data.rerunCondition;
this.optionsPath = data.optionsPath;
@@ -388,9 +398,46 @@
*
* Note that {@link ExecutionService} automatically uses
* the printing processor created by
- * {@link org.netbeans.api.extexecution.input.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * {@link org.netbeans.api.extexecution.print.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
* or
- * {@link org.netbeans.api.extexecution.input.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * {@link org.netbeans.api.extexecution.print.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * (in case {@link #outLineBased(boolean)} is configured to true)
+ * if there is no configured factory.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param outProcessorFactory factory for standard output processor,
+ * null allowed
+ * @return new descriptor with configured factory for additional
+ * processor to use for standard output
+ * @deprecated use {@link #outProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory2)}
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExecutionDescriptor outProcessorFactory(@NullAllowed InputProcessorFactory outProcessorFactory) {
+ DescriptorData data = new DescriptorData(this);
+ return new ExecutionDescriptor(data.outProcessorFactory(outProcessorFactory));
+ }
+
+ InputProcessorFactory getOutProcessorFactory() {
+ return outProcessorFactory;
+ }
+
+ /**
+ * Returns a descriptor with configured factory for standard output
+ * processor. The factory is used by {@link ExecutionService} to create
+ * additional processor for standard output. The configured value will
+ * be ignored if you previously configured processor via deprecated
+ * {@link #outProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory)}.
+ *
+ * Note that {@link ExecutionService} automatically uses
+ * the printing processor created by
+ * {@link org.netbeans.api.extexecution.print.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * or
+ * {@link org.netbeans.api.extexecution.print.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
* (in case {@link #outLineBased(boolean)} is configured to true)
* if there is no configured factory.
*
@@ -406,13 +453,16 @@
*/
@NonNull
@CheckReturnValue
- public ExecutionDescriptor outProcessorFactory(@NullAllowed InputProcessorFactory outProcessorFactory) {
+ public ExecutionDescriptor outProcessorFactory(@NullAllowed InputProcessorFactory2 outProcessorFactory) {
+ if (errProcessorFactory != null) {
+ LOGGER.log(Level.WARNING, "The factory will be ignored as legacy InputProcessorFactory is already defined");
+ }
DescriptorData data = new DescriptorData(this);
- return new ExecutionDescriptor(data.outProcessorFactory(outProcessorFactory));
+ return new ExecutionDescriptor(data.outProcessorFactory(outProcessorFactory2));
}
- InputProcessorFactory getOutProcessorFactory() {
- return outProcessorFactory;
+ InputProcessorFactory2 getOutProcessorFactory2() {
+ return outProcessorFactory2;
}
/**
@@ -422,9 +472,46 @@
*
* Note that {@link ExecutionService} automatically uses
* the printing processor created by
- * {@link org.netbeans.api.extexecution.input.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * {@link org.netbeans.api.extexecution.print.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
* or
- * {@link org.netbeans.api.extexecution.input.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * {@link org.netbeans.api.extexecution.print.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * (in case {@link #errLineBased(boolean)} is configured to true)
+ * if there is no configured factory.
+ *
+ * The default (not configured) value is null.
+ *
+ * All other properties of the returned descriptor are inherited from
+ * this.
+ *
+ * @param errProcessorFactory factory for standard error output processor,
+ * null allowed
+ * @return new descriptor with configured factory for additional
+ * processor to use for standard error output
+ * @deprecated use {@link #errProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory2)}
+ */
+ @NonNull
+ @CheckReturnValue
+ public ExecutionDescriptor errProcessorFactory(@NullAllowed InputProcessorFactory errProcessorFactory) {
+ DescriptorData data = new DescriptorData(this);
+ return new ExecutionDescriptor(data.errProcessorFactory(errProcessorFactory));
+ }
+
+ InputProcessorFactory getErrProcessorFactory() {
+ return errProcessorFactory;
+ }
+
+ /**
+ * Returns a descriptor with configured factory for standard error output
+ * processor. The factory is used by {@link ExecutionService} to create
+ * additional processor for standard error output. The configured value will
+ * be ignored if you previously configured processor via deprecated
+ * {@link #errProcessorFactory(org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory)}.
+ *
+ * Note that {@link ExecutionService} automatically uses
+ * the printing processor created by
+ * {@link org.netbeans.api.extexecution.print.InputProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
+ * or
+ * {@link org.netbeans.api.extexecution.print.LineProcessors#printing(org.openide.windows.OutputWriter, org.netbeans.api.extexecution.print.LineConvertor, boolean)}
* (in case {@link #errLineBased(boolean)} is configured to true)
* if there is no configured factory.
*
@@ -440,13 +527,16 @@
*/
@NonNull
@CheckReturnValue
- public ExecutionDescriptor errProcessorFactory(@NullAllowed InputProcessorFactory errProcessorFactory) {
+ public ExecutionDescriptor errProcessorFactory(@NullAllowed InputProcessorFactory2 errProcessorFactory) {
+ if (errProcessorFactory != null) {
+ LOGGER.log(Level.WARNING, "The factory will be ignored as legacy InputProcessorFactory is already defined");
+ }
DescriptorData data = new DescriptorData(this);
- return new ExecutionDescriptor(data.errProcessorFactory(errProcessorFactory));
+ return new ExecutionDescriptor(data.errProcessorFactory(errProcessorFactory2));
}
- InputProcessorFactory getErrProcessorFactory() {
- return errProcessorFactory;
+ InputProcessorFactory2 getErrProcessorFactory2() {
+ return errProcessorFactory2;
}
/**
@@ -673,6 +763,7 @@
/**
* Factory creating the input processor.
+ * @deprecated use {@link InputProcessorFactory2}
*/
public interface InputProcessorFactory {
@@ -687,9 +778,26 @@
InputProcessor newInputProcessor(@NonNull InputProcessor defaultProcessor);
}
+
+ /**
+ * Factory creating the input processor.
+ */
+ public interface InputProcessorFactory2 {
+
+ /**
+ * Creates and returns new input processor.
+ *
+ * @param defaultProcessor default processor created by
+ * infrastructure that is printing chars to the output window
+ * @return new input processor
+ */
+ @NonNull
+ org.netbeans.api.extexecution.base.input.InputProcessor newInputProcessor(@NonNull org.netbeans.api.extexecution.base.input.InputProcessor defaultProcessor);
+
+ }
/**
- * Factory creating the line covertor.
+ * Factory creating the line convertor.
*/
public interface LineConvertorFactory {
@@ -732,8 +840,12 @@
private LineConvertorFactory errConvertorFactory;
private InputProcessorFactory outProcessorFactory;
+
+ private InputProcessorFactory2 outProcessorFactory2;
private InputProcessorFactory errProcessorFactory;
+
+ private InputProcessorFactory2 errProcessorFactory2;
private InputOutput inputOutput;
@@ -823,11 +935,21 @@
this.outProcessorFactory = outProcessorFactory;
return this;
}
+
+ public DescriptorData outProcessorFactory(InputProcessorFactory2 outProcessorFactory) {
+ this.outProcessorFactory2 = outProcessorFactory2;
+ return this;
+ }
public DescriptorData errProcessorFactory(InputProcessorFactory errProcessorFactory) {
this.errProcessorFactory = errProcessorFactory;
return this;
}
+
+ public DescriptorData errProcessorFactory(InputProcessorFactory2 errProcessorFactory) {
+ this.errProcessorFactory2 = errProcessorFactory2;
+ return this;
+ }
public DescriptorData outConvertorFactory(LineConvertorFactory convertorFactory) {
this.outConvertorFactory = convertorFactory;
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
@@ -47,48 +47,38 @@
import org.netbeans.modules.extexecution.StopAction;
import org.netbeans.modules.extexecution.RerunAction;
import java.awt.event.ActionEvent;
-import java.io.BufferedInputStream;
import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.io.Reader;
-import java.nio.charset.Charset;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
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.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
-import org.netbeans.modules.extexecution.ProcessInputStream;
import org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory;
+import org.netbeans.api.extexecution.ExecutionDescriptor.InputProcessorFactory2;
import org.netbeans.api.extexecution.ExecutionDescriptor.LineConvertorFactory;
-import org.netbeans.api.extexecution.input.InputProcessor;
-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.LineProcessors;
+import org.netbeans.api.extexecution.base.BaseExecutionDescriptor;
+import org.netbeans.api.extexecution.base.BaseExecutionService;
+import org.netbeans.api.extexecution.base.ParametrizedRunnable;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.InputProcessors;
+import org.netbeans.api.extexecution.print.LineProcessors;
+import org.netbeans.modules.extexecution.input.BaseInputProcessor;
+import org.netbeans.modules.extexecution.input.DelegatingInputProcessor;
import org.openide.util.Cancellable;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
-import org.openide.util.RequestProcessor;
import org.openide.windows.InputOutput;
import org.openide.windows.OutputWriter;
/**
- * Execution service provides the facility to execute the process while
+ * Execution service provides the facility to execute a process while
* displaying the output and handling the input.
*
- * Also see {@link ProcessBuilder#getLocal()}.
*
* @author Petr Hejl
* @see #call()
+ * @deprecated use {@link org.netbeans.api.extexecution.base.ProcessBuilder#getLocal()}
*/
public final class ExternalProcessBuilder implements Callable {
diff --git a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessSupport.java b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessSupport.java
--- a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessSupport.java
+++ b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessSupport.java
@@ -44,6 +44,7 @@
import java.util.Map;
import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.extexecution.base.Processes;
import org.netbeans.modules.extexecution.WrapperProcess;
import org.netbeans.spi.extexecution.destroy.ProcessDestroyPerformer;
import org.openide.util.Lookup;
@@ -55,6 +56,7 @@
*
* @author mkleint
* @since 1.16
+ * @deprecated use {@link Processes}
*/
public final class ExternalProcessSupport {
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
@@ -41,7 +41,6 @@
*/
package org.netbeans.api.extexecution;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -50,6 +49,7 @@
import java.util.concurrent.Callable;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.extexecution.base.Environment;
import org.netbeans.modules.extexecution.ProcessBuilderAccessor;
import org.netbeans.spi.extexecution.ProcessBuilderImplementation;
import org.openide.util.NbBundle;
@@ -74,6 +74,7 @@
*
* @author Petr Hejl
* @since 1.28
+ * @deprecated use {@link org.netbeans.api.extexecution.base.ProcessBuilder}
*/
public final class ProcessBuilder implements Callable {
@@ -88,13 +89,13 @@
private String workingDirectory;
/**GuardedBy("this")*/
- private List arguments = new ArrayList();
+ private final List arguments = new ArrayList();
/**GuardedBy("this")*/
- private List paths = new ArrayList();
+ private final List paths = new ArrayList();
/**GuardedBy("this")*/
- private Map envVariables = new HashMap();
+ private final Map envVariables = new HashMap();
/**GuardedBy("this")*/
private boolean redirectErrorStream;
@@ -232,7 +233,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.
*
@@ -278,20 +279,18 @@
public Process createProcess(String executable, String workingDirectory, List arguments,
List paths, Map environment, boolean redirectErrorStream) throws IOException {
- ExternalProcessBuilder builder = new ExternalProcessBuilder(executable);
- if (workingDirectory != null) {
- builder = builder.workingDirectory(new File(workingDirectory));
- }
- for (String argument : arguments) {
- builder = builder.addArgument(argument);
- }
+ org.netbeans.api.extexecution.base.ProcessBuilder builder = org.netbeans.api.extexecution.base.ProcessBuilder.getLocal();
+ builder.setExecutable(executable);
+ builder.setWorkingDirectory(workingDirectory);
+ builder.setArguments(arguments);
+ builder.setRedirectErrorStream(redirectErrorStream);
+ Environment env = builder.getEnvironment();
for (String path : paths) {
- builder = builder.prependPath(new File(path));
+ env.prependPath("PATH", path);
}
for (Map.Entry entry : environment.entrySet()) {
- builder = builder.addEnvironmentVariable(entry.getKey(), entry.getValue());
+ env.setVariable(entry.getKey(), entry.getValue());
}
- builder = builder.redirectErrorStream(redirectErrorStream);
return builder.call();
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputProcessor.java b/extexecution/src/org/netbeans/api/extexecution/input/InputProcessor.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/InputProcessor.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/InputProcessor.java
@@ -54,6 +54,7 @@
*
* @author Petr Hejl
* @see InputReader
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.InputProcessor}
*/
public interface InputProcessor extends Closeable {
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputProcessors.java b/extexecution/src/org/netbeans/api/extexecution/input/InputProcessors.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/InputProcessors.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/InputProcessors.java
@@ -42,30 +42,24 @@
package org.netbeans.api.extexecution.input;
-import java.io.IOException;
+import org.netbeans.modules.extexecution.input.BaseInputProcessor;
+import org.netbeans.modules.extexecution.input.DelegatingInputProcessor;
import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
-import org.netbeans.api.extexecution.print.ConvertedLine;
import org.netbeans.api.extexecution.print.LineConvertor;
-import org.netbeans.modules.extexecution.input.LineParsingHelper;
-import org.openide.util.Parameters;
import org.openide.windows.OutputWriter;
/**
* Factory methods for {@link InputProcessor} classes.
*
* @author Petr Hejl
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.InputProcessors}
+ * and {@link org.netbeans.api.extexecution.print.InputProcessors}
*/
public final class InputProcessors {
- private static final Logger LOGGER = Logger.getLogger(InputProcessors.class.getName());
-
private InputProcessors() {
super();
}
@@ -84,7 +78,7 @@
*/
@NonNull
public static InputProcessor bridge(@NonNull LineProcessor lineProcessor) {
- return new Bridge(lineProcessor);
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.base.input.InputProcessors.bridge(new LineProcessors.BaseLineProcessor(lineProcessor)));
}
/**
@@ -100,7 +94,15 @@
*/
@NonNull
public static InputProcessor proxy(@NonNull InputProcessor... processors) {
- return new ProxyInputProcessor(processors);
+ org.netbeans.api.extexecution.base.input.InputProcessor[] wrapped = new org.netbeans.api.extexecution.base.input.InputProcessor[processors.length];
+ for (int i = 0; i < processors.length; i++) {
+ if (processors[i] != null) {
+ wrapped[i] = new BaseInputProcessor(processors[i]);
+ } else {
+ wrapped[i] = null;
+ }
+ }
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.base.input.InputProcessors.proxy(wrapped));
}
/**
@@ -118,7 +120,7 @@
*/
@NonNull
public static InputProcessor copying(@NonNull Writer writer) {
- return new CopyingInputProcessor(writer);
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.base.input.InputProcessors.copying(writer));
}
/**
@@ -139,7 +141,7 @@
*/
@NonNull
public static InputProcessor printing(@NonNull OutputWriter out, boolean resetEnabled) {
- return printing(out, null, resetEnabled);
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.print.InputProcessors.printing(out, resetEnabled));
}
/**
@@ -166,7 +168,7 @@
*/
@NonNull
public static InputProcessor printing(@NonNull OutputWriter out, @NullAllowed LineConvertor convertor, boolean resetEnabled) {
- return new PrintingInputProcessor(out, convertor, resetEnabled);
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.print.InputProcessors.printing(out, convertor, resetEnabled));
}
/**
@@ -186,297 +188,7 @@
*/
@NonNull
public static InputProcessor ansiStripping(@NonNull InputProcessor delegate) {
- return new AnsiStrippingInputProcessor(delegate);
+ return new DelegatingInputProcessor(org.netbeans.api.extexecution.base.input.InputProcessors.ansiStripping(new BaseInputProcessor(delegate)));
}
-
- private static class Bridge implements InputProcessor {
-
- private final LineProcessor lineProcessor;
-
- private final LineParsingHelper helper = new LineParsingHelper();
-
- private boolean closed;
-
- public Bridge(LineProcessor lineProcessor) {
- Parameters.notNull("lineProcessor", lineProcessor);
-
- this.lineProcessor = lineProcessor;
- }
-
- public final void processInput(char[] chars) {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- String[] lines = helper.parse(chars);
- for (String line : lines) {
- lineProcessor.processLine(line);
- }
- }
-
- public final void reset() {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- flush();
- lineProcessor.reset();
- }
-
- public final void close() {
- closed = true;
-
- flush();
- lineProcessor.close();
- }
-
- private void flush() {
- String line = helper.getTrailingLine(true);
- if (line != null) {
- lineProcessor.processLine(line);
- }
- }
- }
-
- private static class ProxyInputProcessor implements InputProcessor {
-
- private final List processors = new ArrayList();
-
- private boolean closed;
-
- public ProxyInputProcessor(InputProcessor... processors) {
- for (InputProcessor processor : processors) {
- if (processor != null) {
- this.processors.add(processor);
- }
- }
- }
-
- public void processInput(char[] chars) throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- for (InputProcessor processor : processors) {
- processor.processInput(chars);
- }
- }
-
- public void reset() throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- for (InputProcessor processor : processors) {
- processor.reset();
- }
- }
-
- public void close() throws IOException {
- closed = true;
-
- for (InputProcessor processor : processors) {
- processor.close();
- }
- }
- }
-
- private static class PrintingInputProcessor implements InputProcessor {
-
- private final OutputWriter out;
-
- private final LineConvertor convertor;
-
- private final boolean resetEnabled;
-
- private final LineParsingHelper helper = new LineParsingHelper();
-
- private boolean closed;
-
- public PrintingInputProcessor(OutputWriter out, LineConvertor convertor,
- boolean resetEnabled) {
-
- assert out != null;
-
- this.out = out;
- this.convertor = convertor;
- this.resetEnabled = resetEnabled;
- }
-
- public void processInput(char[] chars) {
- assert chars != null;
-
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
-// TODO this does not color standard error lines :(
-// if (convertor == null) {
-// out.print(String.valueOf(chars));
-// return;
-// }
-
- String[] lines = helper.parse(chars);
- for (String line : lines) {
- LOGGER.log(Level.FINEST, "{0}\\n", line);
-
- convert(line);
- out.flush();
- }
-
- String line = helper.getTrailingLine(true);
- if (line != null) {
- LOGGER.log(Level.FINEST, line);
-
- out.print(line);
- out.flush();
- }
- }
-
- public void reset() throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- if (!resetEnabled) {
- return;
- }
-
- out.reset();
- }
-
- public void close() throws IOException {
- closed = true;
-
- out.close();
- }
-
- private void convert(String line) {
- if (convertor == null) {
- out.println(line);
- return;
- }
-
- List convertedLines = convertor.convert(line);
- if (convertedLines == null) {
- out.println(line);
- return;
- }
-
- for (ConvertedLine converted : convertedLines) {
- if (converted.getListener() == null) {
- out.println(converted.getText());
- } else {
- try {
- out.println(converted.getText(), converted.getListener());
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, null, ex);
- out.println(converted.getText());
- }
- }
- }
- }
- }
-
- private static class CopyingInputProcessor implements InputProcessor {
-
- private final Writer writer;
-
- private boolean closed;
-
- public CopyingInputProcessor(Writer writer) {
- this.writer = writer;
- }
-
- public void processInput(char[] chars) throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- LOGGER.log(Level.FINEST, Arrays.toString(chars));
- writer.write(chars);
- writer.flush();
- }
-
- public void reset() {
- // noop
- }
-
- public void close() throws IOException {
- closed = true;
-
- writer.close();
- }
- }
-
- private static class AnsiStrippingInputProcessor implements InputProcessor {
-
- private final InputProcessor delegate;
-
- private boolean closed;
-
- public AnsiStrippingInputProcessor(InputProcessor delegate) {
- this.delegate = delegate;
- }
-
- public void processInput(char[] chars) throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- // FIXME optimize me
- String sequence = new String(chars);
- if (containsAnsiColors(sequence)) {
- sequence = stripAnsiColors(sequence);
- }
- delegate.processInput(sequence.toCharArray());
- }
-
- public void reset() throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- delegate.reset();
- }
-
- public void close() throws IOException {
- closed = true;
-
- delegate.close();
- }
-
- private static boolean containsAnsiColors(String sequence) {
- // RSpec will color output with ANSI color sequence terminal escapes
- return sequence.indexOf("\033[") != -1; // NOI18N
- }
-
- private static String stripAnsiColors(String sequence) {
- StringBuilder sb = new StringBuilder(sequence.length());
- int index = 0;
- int max = sequence.length();
- while (index < max) {
- int nextEscape = sequence.indexOf("\033[", index); // NOI18N
- if (nextEscape == -1) {
- nextEscape = sequence.length();
- }
-
- for (int n = (nextEscape == -1) ? max : nextEscape; index < n; index++) {
- sb.append(sequence.charAt(index));
- }
-
- if (nextEscape != -1) {
- for (; index < max; index++) {
- char c = sequence.charAt(index);
- if (c == 'm') {
- index++;
- break;
- }
- }
- }
- }
-
- return sb.toString();
- }
- }
+
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputReader.java b/extexecution/src/org/netbeans/api/extexecution/input/InputReader.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/InputReader.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/InputReader.java
@@ -54,6 +54,7 @@
* interface has to be responsive to interruption.
*
* @author Petr Hejl
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.InputReader}
*/
public interface InputReader extends Closeable {
@@ -72,6 +73,7 @@
/**
* Closes the reader releasing the resources held by it.
*/
+ @Override
void close() throws IOException;
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputReaderTask.java b/extexecution/src/org/netbeans/api/extexecution/input/InputReaderTask.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/InputReaderTask.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/InputReaderTask.java
@@ -44,9 +44,9 @@
package org.netbeans.api.extexecution.input;
+import org.netbeans.modules.extexecution.input.BaseInputProcessor;
+import org.netbeans.modules.extexecution.input.DelegatingInputProcessor;
import java.io.IOException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.openide.util.Cancellable;
@@ -97,31 +97,14 @@
*
*
* @author Petr Hejl
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.InputReaderTask}
*/
public final class InputReaderTask implements Runnable, Cancellable {
- private static final Logger LOGGER = Logger.getLogger(InputReaderTask.class.getName());
+ private final org.netbeans.api.extexecution.base.input.InputReaderTask delegate;
- private static final int MIN_DELAY = 50;
-
- private static final int MAX_DELAY = 300;
-
- private static final int DELAY_INCREMENT = 50;
-
- private final InputReader inputReader;
-
- private final InputProcessor inputProcessor;
-
- private final boolean draining;
-
- private boolean cancelled;
-
- private boolean running;
-
- private InputReaderTask(InputReader inputReader, InputProcessor inputProcessor, boolean draining) {
- this.inputReader = inputReader;
- this.inputProcessor = inputProcessor;
- this.draining = draining;
+ private InputReaderTask(org.netbeans.api.extexecution.base.input.InputReaderTask delegate) {
+ this.delegate = delegate;
}
/**
@@ -138,7 +121,8 @@
public static InputReaderTask newTask(@NonNull InputReader reader, @NullAllowed InputProcessor processor) {
Parameters.notNull("reader", reader);
- return new InputReaderTask(reader, processor, false);
+ return new InputReaderTask(org.netbeans.api.extexecution.base.input.InputReaderTask.newTask(
+ new BaseInputReader(reader), processor == null ? null : new BaseInputProcessor(processor)));
}
/**
@@ -156,7 +140,8 @@
public static InputReaderTask newDrainingTask(@NonNull InputReader reader, @NullAllowed InputProcessor processor) {
Parameters.notNull("reader", reader);
- return new InputReaderTask(reader, processor, true);
+ return new InputReaderTask(org.netbeans.api.extexecution.base.input.InputReaderTask.newDrainingTask(
+ new BaseInputReader(reader), processor == null ? null : new BaseInputProcessor(processor)));
}
/**
@@ -166,89 +151,7 @@
* It is not allowed to invoke run multiple times.
*/
public void run() {
- synchronized (this) {
- if (running) {
- throw new IllegalStateException("Already running task");
- }
- running = true;
- }
-
- boolean interrupted = false;
- try {
- long delay = MIN_DELAY;
- int emptyReads = 0;
-
- while (true) {
- synchronized (this) {
- if (Thread.currentThread().isInterrupted() || cancelled) {
- interrupted = Thread.interrupted();
- break;
- }
- }
-
- int count = inputReader.readInput(inputProcessor);
-
- // compute the delay based on how often we really get the data
- if (count > 0) {
- delay = MIN_DELAY;
- emptyReads = 0;
- } else {
- // increase the delay only slowly - once for
- // MAX_DELAY / DELAY_INCREMENT unsuccesfull read attempts
- if (emptyReads > (MAX_DELAY / DELAY_INCREMENT)) {
- emptyReads = 0;
- delay = Math.min(delay + DELAY_INCREMENT, MAX_DELAY);
- } else {
- emptyReads++;
- }
- }
-
- if (LOGGER.isLoggable(Level.FINEST)) {
- LOGGER.log(Level.FINEST, "Task {0} sleeping for {1} ms",
- new Object[] {Thread.currentThread().getName(), delay});
- }
- try {
- // give the producer some time to write the output
- Thread.sleep(delay);
- } catch (InterruptedException e) {
- interrupted = true;
- break;
- }
- }
-
- synchronized (this) {
- if (Thread.currentThread().isInterrupted() || cancelled) {
- interrupted = Thread.interrupted();
- }
- }
- } catch (Exception ex) {
- LOGGER.log(Level.FINE, null, ex);
- } finally {
- // drain the rest
- if (draining) {
- try {
- while (inputReader.readInput(inputProcessor) > 0) {
- LOGGER.log(Level.FINE, "Draining the rest of the reader");
- }
- } catch (IOException ex) {
- LOGGER.log(Level.FINE, null, ex);
- }
- }
-
- // perform cleanup
- try {
- if (inputProcessor != null) {
- inputProcessor.close();
- }
- inputReader.close();
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, null, ex);
- } finally {
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ delegate.run();
}
/**
@@ -258,12 +161,31 @@
* @return true if the task was successfully cancelled
*/
public boolean cancel() {
- synchronized (this) {
- if (cancelled) {
- return false;
+ return delegate.cancel();
+ }
+
+ private static class BaseInputReader implements org.netbeans.api.extexecution.base.input.InputReader {
+
+ private final InputReader delegate;
+
+ public BaseInputReader(InputReader delegate) {
+ this.delegate = delegate;
+ }
+
+ public int readInput(org.netbeans.api.extexecution.base.input.InputProcessor processor) throws IOException {
+ InputProcessor p = null;
+ if (processor != null) {
+ if (processor instanceof BaseInputProcessor) {
+ p = ((BaseInputProcessor) processor).getDelegate();
+ } else {
+ p = new DelegatingInputProcessor(processor);
+ }
}
- cancelled = true;
- return true;
+ return delegate.readInput(p);
+ }
+
+ public void close() throws IOException {
+ delegate.close();
}
}
}
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
@@ -42,21 +42,23 @@
package org.netbeans.api.extexecution.input;
+import org.netbeans.modules.extexecution.input.BaseInputProcessor;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
-import org.netbeans.modules.extexecution.input.FileInputReader;
-import org.netbeans.modules.extexecution.input.DefaultInputReader;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
import org.openide.util.Parameters;
/**
* Factory methods for {@link InputReader} classes.
*
* @author Petr Hejl
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.InputReaders}
*/
public final class InputReaders {
@@ -82,7 +84,19 @@
*/
@NonNull
public static InputReader forReader(@NonNull Reader reader) {
- return new DefaultInputReader(reader, true);
+ final org.netbeans.api.extexecution.base.input.InputReader delegate = org.netbeans.api.extexecution.base.input.InputReaders.forReader(reader);
+ return new InputReader() {
+
+ @Override
+ public int readInput(org.netbeans.api.extexecution.input.InputProcessor processor) throws IOException {
+ return delegate.readInput(processor == null ? null : new BaseInputProcessor(processor));
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+ };
}
/**
@@ -157,10 +171,29 @@
* @return input reader for the given provider
*/
@NonNull
- public static InputReader forFileInputProvider(@NonNull FileInput.Provider fileProvider) {
+ public static InputReader forFileInputProvider(@NonNull final FileInput.Provider fileProvider) {
Parameters.notNull("fileProvider", fileProvider);
- return new FileInputReader(fileProvider);
+ final org.netbeans.api.extexecution.base.input.InputReader delegate = org.netbeans.api.extexecution.base.input.InputReaders.forFileInputProvider(new org.netbeans.api.extexecution.base.input.InputReaders.FileInput.Provider() {
+
+ @Override
+ public org.netbeans.api.extexecution.base.input.InputReaders.FileInput getFileInput() {
+ FileInput input = fileProvider.getFileInput();
+ return new org.netbeans.api.extexecution.base.input.InputReaders.FileInput(input.getFile(), input.getCharset());
+ }
+ });
+ return new InputReader() {
+
+ @Override
+ public int readInput(org.netbeans.api.extexecution.input.InputProcessor processor) throws IOException {
+ return delegate.readInput(processor == null ? null : new BaseInputProcessor(processor));
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+ };
}
/**
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/LineProcessor.java b/extexecution/src/org/netbeans/api/extexecution/input/LineProcessor.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/LineProcessor.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/LineProcessor.java
@@ -57,6 +57,7 @@
* @author Petr Hejl
* @see InputProcessors#bridge(LineProcessor)
* @see InputReader
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.LineProcessor}
*/
public interface LineProcessor extends Closeable {
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/LineProcessors.java b/extexecution/src/org/netbeans/api/extexecution/input/LineProcessors.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/LineProcessors.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/LineProcessors.java
@@ -42,16 +42,11 @@
package org.netbeans.api.extexecution.input;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
-import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
-import org.netbeans.api.extexecution.print.ConvertedLine;
import org.netbeans.api.extexecution.print.LineConvertor;
import org.openide.windows.OutputWriter;
@@ -63,11 +58,11 @@
*
* @author Petr Hejl
* @see InputProcessors#bridge(org.netbeans.api.extexecution.input.LineProcessor)
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input.LineProcessors}
+ * and {@link org.netbeans.api.extexecution.print.LineProcessors}
*/
public final class LineProcessors {
- private static final Logger LOGGER = Logger.getLogger(LineProcessors.class.getName());
-
private LineProcessors() {
super();
}
@@ -85,7 +80,15 @@
*/
@NonNull
public static LineProcessor proxy(@NonNull LineProcessor... processors) {
- return new ProxyLineProcessor(processors);
+ org.netbeans.api.extexecution.base.input.LineProcessor[] wrapped = new org.netbeans.api.extexecution.base.input.LineProcessor[processors.length];
+ for (int i = 0; i < processors.length; i++) {
+ if (processors[i] != null) {
+ wrapped[i] = new BaseLineProcessor(processors[i]);
+ } else {
+ wrapped[i] = null;
+ }
+ }
+ return new DelegatingLineProcessor(org.netbeans.api.extexecution.base.input.LineProcessors.proxy(wrapped));
}
/**
@@ -106,7 +109,7 @@
*/
@NonNull
public static LineProcessor printing(@NonNull OutputWriter out, boolean resetEnabled) {
- return printing(out, null, resetEnabled);
+ return new DelegatingLineProcessor(org.netbeans.api.extexecution.print.LineProcessors.printing(out, resetEnabled));
}
/**
@@ -132,7 +135,7 @@
*/
@NonNull
public static LineProcessor printing(@NonNull OutputWriter out, @NullAllowed LineConvertor convertor, boolean resetEnabled) {
- return new PrintingLineProcessor(out, convertor, resetEnabled);
+ return new DelegatingLineProcessor(org.netbeans.api.extexecution.print.LineProcessors.printing(out, convertor, resetEnabled));
}
/**
@@ -151,168 +154,54 @@
*/
@NonNull
public static LineProcessor patternWaiting(@NonNull Pattern pattern, @NonNull CountDownLatch latch) {
- return new WaitingLineProcessor(pattern, latch);
+ return new DelegatingLineProcessor(org.netbeans.api.extexecution.base.input.LineProcessors.patternWaiting(pattern, latch));
}
+
+ static class DelegatingLineProcessor implements LineProcessor {
+
+ private final org.netbeans.api.extexecution.base.input.LineProcessor delegate;
- private static class ProxyLineProcessor implements LineProcessor {
-
- private final List processors = new ArrayList();
-
- private boolean closed;
-
- public ProxyLineProcessor(LineProcessor... processors) {
- for (LineProcessor processor : processors) {
- if (processor != null) {
- this.processors.add(processor);
- }
- }
+ public DelegatingLineProcessor(org.netbeans.api.extexecution.base.input.LineProcessor delegate) {
+ this.delegate = delegate;
}
+ @Override
public void processLine(String line) {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- for (LineProcessor processor : processors) {
- processor.processLine(line);
- }
+ delegate.processLine(line);
}
+ @Override
public void reset() {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- for (LineProcessor processor : processors) {
- processor.reset();
- }
+ delegate.reset();
}
+ @Override
public void close() {
- closed = true;
-
- for (LineProcessor processor : processors) {
- processor.close();
- }
+ delegate.close();
}
}
+
+ static class BaseLineProcessor implements org.netbeans.api.extexecution.base.input.LineProcessor {
+
+ private final LineProcessor delegate;
- private static class PrintingLineProcessor implements LineProcessor {
-
- private final OutputWriter out;
-
- private final LineConvertor convertor;
-
- private final boolean resetEnabled;
-
- private boolean closed;
-
- public PrintingLineProcessor(OutputWriter out, LineConvertor convertor, boolean resetEnabled) {
- assert out != null;
-
- this.out = out;
- this.convertor = convertor;
- this.resetEnabled = resetEnabled;
+ public BaseLineProcessor(LineProcessor delegate) {
+ this.delegate = delegate;
}
+ @Override
public void processLine(String line) {
- assert line != null;
-
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- LOGGER.log(Level.FINEST, line);
-
- if (convertor != null) {
- List convertedLines = convertor.convert(line);
- if (convertedLines != null) {
- for (ConvertedLine converted : convertedLines) {
- if (converted.getListener() == null) {
- out.println(converted.getText());
- } else {
- try {
- out.println(converted.getText(), converted.getListener());
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, null, ex);
- out.println(converted.getText());
- }
- }
- }
- } else {
- out.println(line);
- }
- } else {
- out.println(line);
- }
- out.flush();
+ delegate.processLine(line);
}
+ @Override
public void reset() {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- if (!resetEnabled) {
- return;
- }
-
- try {
- out.reset();
- } catch (IOException ex) {
- LOGGER.log(Level.INFO, null, ex);
- }
+ delegate.reset();
}
+ @Override
public void close() {
- closed = true;
-
- out.flush();
- out.close();
- }
- }
-
- private static class WaitingLineProcessor implements LineProcessor {
-
- private final Pattern pattern;
-
- private final CountDownLatch latch;
-
- /**GuardedBy("this")*/
- private boolean processed;
-
- /**GuardedBy("this")*/
- private boolean closed;
-
- public WaitingLineProcessor(Pattern pattern, CountDownLatch latch) {
- assert pattern != null;
- assert latch != null;
-
- this.pattern = pattern;
- this.latch = latch;
- }
-
- public synchronized void processLine(String line) {
- assert line != null;
-
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
-
- if (!processed && pattern.matcher(line).matches()) {
- latch.countDown();
- processed = true;
- }
- }
-
- public synchronized void reset() {
- if (closed) {
- throw new IllegalStateException("Already closed processor");
- }
- }
-
- public synchronized void close() {
- closed = true;
+ delegate.close();
}
}
}
diff --git a/extexecution/src/org/netbeans/api/extexecution/input/package-info.java b/extexecution/src/org/netbeans/api/extexecution/input/package-info.java
--- a/extexecution/src/org/netbeans/api/extexecution/input/package-info.java
+++ b/extexecution/src/org/netbeans/api/extexecution/input/package-info.java
@@ -48,6 +48,7 @@
* is character or line based.
*
* @see org.netbeans.api.extexecution.input.InputReaderTask
+ * @deprecated use {@link org.netbeans.api.extexecution.base.input}
*/
package org.netbeans.api.extexecution.input;
diff --git a/extexecution/src/org/netbeans/api/extexecution/print/InputProcessors.java b/extexecution/src/org/netbeans/api/extexecution/print/InputProcessors.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/print/InputProcessors.java
@@ -0,0 +1,214 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.print;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.modules.extexecution.input.LineParsingHelper;
+import org.openide.windows.OutputWriter;
+
+/**
+ * Factory methods for {@link InputProcessor} classes.
+ *
+ * @author Petr Hejl
+ * @since 1.43
+ */
+public final class InputProcessors {
+
+ private static final Logger LOGGER = Logger.getLogger(InputProcessors.class.getName());
+
+ private InputProcessors() {
+ super();
+ }
+
+ /**
+ * Returns the processor printing all characters passed for processing to
+ * the given output writer.
+ *
+ * Reset action on the returned processor resets the writer if it is enabled
+ * by passing true as resetEnabled. Processor
+ * closes the output writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print received characters
+ * @param resetEnabled determines whether the reset operation will work
+ * (will reset the writer if so)
+ * @return the processor printing all characters passed for processing to
+ * the given output writer
+ */
+ @NonNull
+ public static InputProcessor printing(@NonNull OutputWriter out, boolean resetEnabled) {
+ return printing(out, null, resetEnabled);
+ }
+
+ /**
+ * Returns the processor converting whole lines with convertor and
+ * printing the result including unterminated tail (if present) to the
+ * given output writer. If the convertor does not handle line passed to it
+ * (returning null) raw lines are printed.
+ *
+ * Reset action on the returned processor resets the writer if it is enabled
+ * by passing true as resetEnabled. Processor
+ * closes the output writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print converted lines and characters
+ * @param convertor convertor converting the whole lines
+ * before printing, may be null
+ * @param resetEnabled determines whether the reset operation will work
+ * (will reset the writer if so)
+ * @return the processor converting the whole lines with convertor and
+ * printing the result including unterminated tail (if present)
+ * to the given output writer
+ * @see LineConvertor
+ */
+ @NonNull
+ public static InputProcessor printing(@NonNull OutputWriter out, @NullAllowed LineConvertor convertor, boolean resetEnabled) {
+ return new PrintingInputProcessor(out, convertor, resetEnabled);
+ }
+
+ private static class PrintingInputProcessor implements InputProcessor {
+
+ private final OutputWriter out;
+
+ private final LineConvertor convertor;
+
+ private final boolean resetEnabled;
+
+ private final LineParsingHelper helper = new LineParsingHelper();
+
+ private boolean closed;
+
+ public PrintingInputProcessor(OutputWriter out, LineConvertor convertor,
+ boolean resetEnabled) {
+
+ assert out != null;
+
+ this.out = out;
+ this.convertor = convertor;
+ this.resetEnabled = resetEnabled;
+ }
+
+ public void processInput(char[] chars) {
+ assert chars != null;
+
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+// TODO this does not color standard error lines :(
+// if (convertor == null) {
+// out.print(String.valueOf(chars));
+// return;
+// }
+
+ String[] lines = helper.parse(chars);
+ for (String line : lines) {
+ LOGGER.log(Level.FINEST, "{0}\\n", line);
+
+ convert(line);
+ out.flush();
+ }
+
+ String line = helper.getTrailingLine(true);
+ if (line != null) {
+ LOGGER.log(Level.FINEST, line);
+
+ out.print(line);
+ out.flush();
+ }
+ }
+
+ public void reset() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ if (!resetEnabled) {
+ return;
+ }
+
+ out.reset();
+ }
+
+ public void close() throws IOException {
+ closed = true;
+
+ out.close();
+ }
+
+ private void convert(String line) {
+ if (convertor == null) {
+ out.println(line);
+ return;
+ }
+
+ List convertedLines = convertor.convert(line);
+ if (convertedLines == null) {
+ out.println(line);
+ return;
+ }
+
+ for (ConvertedLine converted : convertedLines) {
+ if (converted.getListener() == null) {
+ out.println(converted.getText());
+ } else {
+ try {
+ out.println(converted.getText(), converted.getListener());
+ } catch (IOException ex) {
+ LOGGER.log(Level.INFO, null, ex);
+ out.println(converted.getText());
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/extexecution/src/org/netbeans/api/extexecution/print/LineProcessors.java b/extexecution/src/org/netbeans/api/extexecution/print/LineProcessors.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/api/extexecution/print/LineProcessors.java
@@ -0,0 +1,194 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 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]"
+ *
+ * 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 2008 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.api.extexecution.print;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.base.input.LineProcessor;
+import org.openide.windows.OutputWriter;
+
+/**
+ * Factory methods for {@link LineProcessor} classes.
+ *
+ * Note that main difference between {@link InputProcessor} and
+ * {@link LineProcessor} is that LineProcessor always process whole lines.
+ *
+ * @author Petr Hejl
+ * @see org.netbeans.api.extexecution.base.input.InputProcessors#bridge(org.netbeans.api.extexecution.base.input.LineProcessor)
+ * @since 1.43
+ */
+public final class LineProcessors {
+
+ private static final Logger LOGGER = Logger.getLogger(LineProcessors.class.getName());
+
+ private LineProcessors() {
+ super();
+ }
+
+ /**
+ * Returns the processor printing all lines passed for processing to
+ * the given output writer.
+ *
+ * Reset action on the returned processor resets the writer if it is enabled
+ * by passing true as resetEnabled. Processor
+ * closes the output writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print received lines
+ * @param resetEnabled determines whether the reset operation will work
+ * (will reset the writer if so)
+ * @return the processor printing all lines passed for processing to
+ * the given output writer
+ */
+ @NonNull
+ public static LineProcessor printing(@NonNull OutputWriter out, boolean resetEnabled) {
+ return printing(out, null, resetEnabled);
+ }
+
+ /**
+ * Returns the processor converting lines with convertor and
+ * printing the result to the given output writer. If the covertor does
+ * not handle line passed to it (returning null) raw
+ * lines are printed.
+ *
+ * Reset action on the returned processor resets the writer if it is enabled
+ * by passing true as resetEnabled. Processor
+ * closes the output writer on {@link InputProcessor#close()}.
+ *
+ * Returned processor is not thread safe.
+ *
+ * @param out where to print converted lines and characters
+ * @param convertor convertor converting the lines before printing,
+ * may be null
+ * @param resetEnabled determines whether the reset operation will work
+ * (will reset the writer if so)
+ * @return the processor converting the lines with convertor and
+ * printing the result to the given output writer
+ * @see LineConvertor
+ */
+ @NonNull
+ public static LineProcessor printing(@NonNull OutputWriter out, @NullAllowed LineConvertor convertor, boolean resetEnabled) {
+ return new PrintingLineProcessor(out, convertor, resetEnabled);
+ }
+
+ private static class PrintingLineProcessor implements LineProcessor {
+
+ private final OutputWriter out;
+
+ private final LineConvertor convertor;
+
+ private final boolean resetEnabled;
+
+ private boolean closed;
+
+ public PrintingLineProcessor(OutputWriter out, LineConvertor convertor, boolean resetEnabled) {
+ assert out != null;
+
+ this.out = out;
+ this.convertor = convertor;
+ this.resetEnabled = resetEnabled;
+ }
+
+ public void processLine(String line) {
+ assert line != null;
+
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ LOGGER.log(Level.FINEST, line);
+
+ if (convertor != null) {
+ List convertedLines = convertor.convert(line);
+ if (convertedLines != null) {
+ for (ConvertedLine converted : convertedLines) {
+ if (converted.getListener() == null) {
+ out.println(converted.getText());
+ } else {
+ try {
+ out.println(converted.getText(), converted.getListener());
+ } catch (IOException ex) {
+ LOGGER.log(Level.INFO, null, ex);
+ out.println(converted.getText());
+ }
+ }
+ }
+ } else {
+ out.println(line);
+ }
+ } else {
+ out.println(line);
+ }
+ out.flush();
+ }
+
+ public void reset() {
+ if (closed) {
+ throw new IllegalStateException("Already closed processor");
+ }
+
+ if (!resetEnabled) {
+ return;
+ }
+
+ try {
+ out.reset();
+ } catch (IOException ex) {
+ LOGGER.log(Level.INFO, null, ex);
+ }
+ }
+
+ public void close() {
+ closed = true;
+
+ out.flush();
+ out.close();
+ }
+ }
+}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/ProcessInputStream.java b/extexecution/src/org/netbeans/modules/extexecution/ProcessInputStream.java
deleted file mode 100644
--- a/extexecution/src/org/netbeans/modules/extexecution/ProcessInputStream.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 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]"
- *
- * 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 2008 Sun Microsystems, Inc.
- */
-
-package org.netbeans.modules.extexecution;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- *
- * @author Petr Hejl
- */
-public final class ProcessInputStream extends FilterInputStream {
-
- private static final Logger LOGGER = Logger.getLogger(ProcessInputStream.class.getName());
-
- private final Process process;
-
- private byte[] buffer;
-
- private int position;
-
- private boolean closed;
-
- private boolean exhausted;
-
- public ProcessInputStream(Process process, InputStream in) {
- super(in);
- this.process = process;
- }
-
- @Override
- public synchronized int available() throws IOException {
- if (buffer != null && position < buffer.length) {
- return buffer.length - position;
- } else if (closed) {
- if (!exhausted) {
- exhausted = true;
- return 0;
- } else {
- throw new IOException("Already closed stream");
- }
- }
- return super.available();
- }
-
- @Override
- public synchronized void close() throws IOException {
- if (!closed) {
- close(false);
- }
- }
-
- @Override
- public void mark(int readlimit) {
- // noop
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
- @Override
- public synchronized int read() throws IOException {
- if (buffer != null && position < buffer.length) {
- return buffer[position++];
- } else if (closed) {
- if (!exhausted) {
- exhausted = true;
- return -1;
- } else {
- throw new IOException("Already closed stream");
- }
- }
- return super.read();
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public synchronized int read(byte[] b, int off, int len) throws IOException {
- if (buffer != null) {
- int available = buffer.length - position;
- int size = Math.min(len, available);
- System.arraycopy(buffer, position, b, off, size);
- position += size;
- return size;
- } else if (closed) {
- if (!exhausted) {
- exhausted = true;
- return -1;
- } else {
- throw new IOException("Already closed stream");
- }
- }
- return super.read(b, off, len);
- }
-
- @Override
- public void reset() throws IOException {
- // noop
- }
-
- @Override
- public long skip(long n) throws IOException {
- return 0;
- }
-
- public synchronized void close(boolean drain) throws IOException {
- closed = true;
-
- if (drain) {
- LOGGER.log(Level.FINE, "Draining process stream");
-
- boolean running = false;
- try {
- process.exitValue();
- } catch (IllegalThreadStateException ex) {
- running = true;
- }
-
- if (running) {
- LOGGER.log(Level.FINE, "Process is still running");
- }
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- try {
- if (running) {
- while (super.available() > 0) {
- os.write(super.read());
- }
- } else {
- int read;
- // FIXME this occasionaly block forever on Vista :(
- while ((read = super.read()) >= 0) {
- os.write(read);
- }
- }
- } catch (IOException ex) {
- LOGGER.log(Level.FINE, null, ex);
- }
-
- buffer = os.toByteArray();
- LOGGER.log(Level.FINE, "Read {0} bytes from stream", buffer.length);
- }
-
- super.close();
- }
-}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/input/BaseInputProcessor.java b/extexecution/src/org/netbeans/modules/extexecution/input/BaseInputProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/modules/extexecution/input/BaseInputProcessor.java
@@ -0,0 +1,78 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.input;
+
+import java.io.IOException;
+import org.netbeans.api.extexecution.input.InputProcessor;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class BaseInputProcessor implements org.netbeans.api.extexecution.base.input.InputProcessor {
+ private final InputProcessor delegate;
+
+ public BaseInputProcessor(InputProcessor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ delegate.processInput(chars);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ delegate.reset();
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+
+ public InputProcessor getDelegate() {
+ return delegate;
+ }
+
+}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/input/DefaultInputReader.java b/extexecution/src/org/netbeans/modules/extexecution/input/DefaultInputReader.java
deleted file mode 100644
--- a/extexecution/src/org/netbeans/modules/extexecution/input/DefaultInputReader.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-package org.netbeans.modules.extexecution.input;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.netbeans.api.extexecution.input.InputProcessor;
-import org.netbeans.api.extexecution.input.InputReader;
-
-/**
- *
- * This class is NotThreadSafe.
- * @author Petr.Hejl
- */
-public class DefaultInputReader implements InputReader {
-
- private static final Logger LOGGER = Logger.getLogger(DefaultInputReader.class.getName());
-
- private static final int BUFFER_SIZE = 512;
-
- private final Reader reader;
-
- private final char[] buffer;
-
- private final boolean greedy;
-
- private boolean closed;
-
- public DefaultInputReader(Reader reader, boolean greedy) {
- assert reader != null;
-
- this.reader = new BufferedReader(reader);
- this.greedy = greedy;
- this.buffer = new char[greedy ? BUFFER_SIZE * 2 : BUFFER_SIZE];
- }
-
- public int readInput(InputProcessor inputProcessor) throws IOException {
- if (closed) {
- throw new IllegalStateException("Already closed reader");
- }
-
- if (!reader.ready()) {
- return 0;
- }
-
- int fetched = 0;
- // TODO optimization possible
- StringBuilder builder = new StringBuilder();
- do {
- int size = reader.read(buffer);
- if (size > 0) {
- builder.append(buffer, 0, size);
- fetched += size;
- }
- } while (reader.ready() && greedy);
-
- if (inputProcessor != null && fetched > 0) {
- inputProcessor.processInput(builder.toString().toCharArray());
- }
-
- return fetched;
- }
-
- public void close() throws IOException {
- closed = true;
- reader.close();
- LOGGER.log(Level.FINEST, "Reader closed");
- }
-
-}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/input/DelegatingInputProcessor.java b/extexecution/src/org/netbeans/modules/extexecution/input/DelegatingInputProcessor.java
new file mode 100644
--- /dev/null
+++ b/extexecution/src/org/netbeans/modules/extexecution/input/DelegatingInputProcessor.java
@@ -0,0 +1,74 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2014 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 2014 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.extexecution.input;
+
+import java.io.IOException;
+import org.netbeans.api.extexecution.input.InputProcessor;
+
+/**
+ *
+ * @author Petr Hejl
+ */
+public class DelegatingInputProcessor implements InputProcessor {
+ private final org.netbeans.api.extexecution.base.input.InputProcessor delegate;
+
+ public DelegatingInputProcessor(org.netbeans.api.extexecution.base.input.InputProcessor delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void processInput(char[] chars) throws IOException {
+ delegate.processInput(chars);
+ }
+
+ @Override
+ public void reset() throws IOException {
+ delegate.reset();
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+
+}
diff --git a/extexecution/src/org/netbeans/modules/extexecution/input/FileInputReader.java b/extexecution/src/org/netbeans/modules/extexecution/input/FileInputReader.java
deleted file mode 100644
--- a/extexecution/src/org/netbeans/modules/extexecution/input/FileInputReader.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.
- */
-
-package org.netbeans.modules.extexecution.input;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.netbeans.api.extexecution.input.InputProcessor;
-import org.netbeans.api.extexecution.input.InputReader;
-import org.netbeans.api.extexecution.input.InputReaders;
-
-/**
- *
- * This class is NotThreadSafe.
- * @author Petr Hejl
- */
-public class FileInputReader implements InputReader {
-
- private static final Logger LOGGER = Logger.getLogger(FileInputReader.class.getName());
-
- private static final int BUFFER_SIZE = 512;
-
- private final InputReaders.FileInput.Provider fileProvider;
-
- private final char[] buffer = new char[BUFFER_SIZE];
-
- private InputReaders.FileInput currentFile;
-
- private Reader reader;
-
- private long fileLength;
-
- private boolean closed;
-
- public FileInputReader(InputReaders.FileInput.Provider fileProvider) {
- assert fileProvider != null;
-
- this.fileProvider = fileProvider;
- }
-
- public int readInput(InputProcessor inputProcessor) {
- if (closed) {
- throw new IllegalStateException("Already closed reader");
- }
-
- int fetched = 0;
- try {
- InputReaders.FileInput file = fileProvider.getFileInput();
-
- if ((currentFile != file && (currentFile == null || !currentFile.equals(file)))
- || fileLength > currentFile.getFile().length() || reader == null) {
-
- if (reader != null) {
- reader.close();
- }
-
- currentFile = file;
-
- if (currentFile != null && currentFile.getFile().exists()
- && currentFile.getFile().canRead()) {
-
- reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(currentFile.getFile()), currentFile.getCharset()));
- }
- if (fileLength > 0) {
- inputProcessor.reset();
- }
- fileLength = 0;
- }
-
- if (reader == null) {
- return fetched;
- }
-
- int size = reader.read(buffer);
- if (size > 0) {
- fileLength += size;
- fetched += size;
-
- if (inputProcessor != null) {
- char[] toProcess = new char[size];
- System.arraycopy(buffer, 0, toProcess, 0, size);
- inputProcessor.processInput(toProcess);
- }
- }
- } catch (Exception ex) {
- LOGGER.log(Level.INFO, null, ex);
- // we will try the next loop (if any)
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException iex) {
- LOGGER.log(Level.FINE, null, ex);
- }
- }
- }
-
- return fetched;
- }
-
- public void close() throws IOException {
- closed = true;
- if (reader != null) {
- reader.close();
- reader = null;
- }
- }
-
-}
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
@@ -49,6 +49,8 @@
*
* @author Petr Hejl
* @since 1.28
+ * @deprecated use {@link org.netbeans.spi.extexecution.base.ProcessBuilderFactory}
+ * and {@link org.netbeans.spi.extexecution.base.ProcessBuilderImplementation}
*/
public class ProcessBuilderFactory {
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,8 @@
* @see org.netbeans.api.extexecution.ProcessBuilder
* @author Petr Hejl
* @since 1.28
+ * @deprecated use {@link org.netbeans.spi.extexecution.base.ProcessBuilderImplementation}
+ * and {@link org.netbeans.spi.extexecution.base.ProcessBuilderFactory}
*/
public interface ProcessBuilderImplementation {
diff --git a/extexecution/src/org/netbeans/spi/extexecution/destroy/ProcessDestroyPerformer.java b/extexecution/src/org/netbeans/spi/extexecution/destroy/ProcessDestroyPerformer.java
--- a/extexecution/src/org/netbeans/spi/extexecution/destroy/ProcessDestroyPerformer.java
+++ b/extexecution/src/org/netbeans/spi/extexecution/destroy/ProcessDestroyPerformer.java
@@ -43,6 +43,8 @@
package org.netbeans.spi.extexecution.destroy;
import java.util.Map;
+import org.netbeans.api.extexecution.base.Processes;
+import org.netbeans.spi.extexecution.base.ProcessesImplementation;
/**
* A service capable of properly terminating external process along with any
@@ -60,6 +62,7 @@
*
* @author mkleint
* @since 1.16
+ * @deprecated use {@link ProcessesImplementation} and {@link Processes}
*/
public interface ProcessDestroyPerformer {
diff --git a/extexecution/src/org/netbeans/spi/extexecution/destroy/package-info.java b/extexecution/src/org/netbeans/spi/extexecution/destroy/package-info.java
--- a/extexecution/src/org/netbeans/spi/extexecution/destroy/package-info.java
+++ b/extexecution/src/org/netbeans/spi/extexecution/destroy/package-info.java
@@ -46,6 +46,7 @@
* The support SPI for terminating external processes.
*
* @see org.netbeans.spi.extexecution.destroy.ProcessDestroyPerformer
+ * @deprecated use {@link org.netbeans.spi.extexecution.base}
*/
package org.netbeans.spi.extexecution.destroy;
diff --git a/extexecution/src/org/netbeans/spi/extexecution/package-info.java b/extexecution/src/org/netbeans/spi/extexecution/package-info.java
--- a/extexecution/src/org/netbeans/spi/extexecution/package-info.java
+++ b/extexecution/src/org/netbeans/spi/extexecution/package-info.java
@@ -46,6 +46,7 @@
* The support SPI for creation of external processes.
*
* @see org.netbeans.spi.extexecution.ProcessBuilderImplementation
+ * @deprecated use {@link org.netbeans.spi.extexecution.base}
*/
package org.netbeans.spi.extexecution;
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputProcessorsTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/print/InputProcessorsTest.java
copy from extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputProcessorsTest.java
copy to extexecution/test/unit/src/org/netbeans/api/extexecution/print/InputProcessorsTest.java
--- a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputProcessorsTest.java
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/print/InputProcessorsTest.java
@@ -40,15 +40,16 @@
* Portions Copyrighted 2008 Sun Microsystems, Inc.
*/
-package org.netbeans.api.extexecution.input;
+package org.netbeans.api.extexecution.print;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.netbeans.api.extexecution.base.input.InputProcessor;
+import org.netbeans.api.extexecution.input.TestInputWriter;
import org.netbeans.junit.NbTestCase;
/**
@@ -57,86 +58,10 @@
*/
public class InputProcessorsTest extends NbTestCase {
- private static final char[] PROXY_CHARS_CHUNK1 = "abcdefghij".toCharArray();
-
- private static final char[] PROXY_CHARS_CHUNK2 = "jihgfedcba".toCharArray();
-
- private static final char[][] PROXY_TEST_CHARS = new char[][] {
- PROXY_CHARS_CHUNK1, PROXY_CHARS_CHUNK2
- };
-
- private static final List BRIDGE_TEST_LINES = new ArrayList();
-
- private static final char[][] BRIDGE_TEST_CHARS;
-
- static {
- Collections.addAll(BRIDGE_TEST_LINES, "test1", "test2");
-
- BRIDGE_TEST_CHARS = new char[BRIDGE_TEST_LINES.size()][];
- for (int i = 0; i < BRIDGE_TEST_LINES.size(); i++) {
- BRIDGE_TEST_CHARS[i] = (BRIDGE_TEST_LINES.get(i) + "\n").toCharArray();
- }
- }
-
public InputProcessorsTest(String name) {
super(name);
}
- public void testBridge() throws IOException {
- TestLineProcessor processor = new TestLineProcessor(false);
- InputProcessor bridge = InputProcessors.bridge(processor);
-
- for (char[] chunk : BRIDGE_TEST_CHARS) {
- bridge.processInput(chunk);
- }
-
- assertEquals(0, processor.getResetCount());
- assertEquals(BRIDGE_TEST_LINES, processor.getLinesProcessed());
-
- bridge.reset();
- assertEquals(1, processor.getResetCount());
-
- bridge.close();
- assertClosedConditions(bridge);
- assertTrue(processor.isClosed());
- }
-
- public void testProxy() throws IOException {
- TestInputProcessor processor1 = new TestInputProcessor(false);
- TestInputProcessor processor2 = new TestInputProcessor(false);
-
- InputProcessor proxy = InputProcessors.proxy(processor1, processor2);
- int size = 0;
- for (char[] chunk : PROXY_TEST_CHARS) {
- proxy.processInput(chunk);
- size += chunk.length;
- }
-
- char[] expected = new char[size];
- int position = 0;
- for (char[] chunk : PROXY_TEST_CHARS) {
- System.arraycopy(chunk, 0, expected, position, chunk.length);
- position += chunk.length;
- }
-
- assertEquals(0, processor1.getResetCount());
- assertEquals(0, processor2.getResetCount());
-
- assertTrue(Arrays.equals(expected, processor1.getCharsProcessed()));
- assertTrue(Arrays.equals(expected, processor2.getCharsProcessed()));
-
- proxy.reset();
-
- assertEquals(1, processor1.getResetCount());
- assertEquals(1, processor2.getResetCount());
-
- proxy.close();
- assertClosedConditions(proxy);
-
- assertTrue(processor1.isClosed());
- assertTrue(processor2.isClosed());
- }
-
public void testPrinting() throws IOException {
TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
InputProcessor processor = InputProcessors.printing(writer, true);
@@ -191,13 +116,6 @@
assertClosedConditions(processor);
}
- private static void assertEquals(List expected, List value) {
- assertEquals(expected.size(), value.size());
- for (int i = 0; i < expected.size(); i++) {
- assertEquals(expected.get(i), value.get(i));
- }
- }
-
private static void assertClosedConditions(InputProcessor inputProcessor) throws IOException {
try {
inputProcessor.processInput(new char[] {'0'});
diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/LineProcessorsTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/print/LineProcessorsTest.java
copy from extexecution/test/unit/src/org/netbeans/api/extexecution/input/LineProcessorsTest.java
copy to extexecution/test/unit/src/org/netbeans/api/extexecution/print/LineProcessorsTest.java
--- a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/LineProcessorsTest.java
+++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/print/LineProcessorsTest.java
@@ -40,24 +40,18 @@
* Portions Copyrighted 2008 Sun Microsystems, Inc.
*/
-package org.netbeans.api.extexecution.input;
+package org.netbeans.api.extexecution.print;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Random;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.regex.Pattern;
+import org.netbeans.api.extexecution.base.input.LineProcessor;
+import org.netbeans.api.extexecution.input.TestInputWriter;
import org.netbeans.junit.NbTestCase;
-import org.netbeans.junit.RandomlyFails;
/**
*
@@ -65,22 +59,9 @@
*/
public class LineProcessorsTest extends NbTestCase {
- private static final String WAIT_RELEASE_STRING = "test"; // NOI18N
-
- private static final long DEADLOCK_TIMEOUT = 1000;
-
- private static final int WAIT_THREAD_COUNT = 5;
-
- private static final int PRODUCER_THREAD_COUNT = 5;
-
- private static final long TEST_TIMEOUT = 5000;
-
- private static final List PROXY_TEST_LINES = new ArrayList();
-
private static final List PRINTING_TEST_LINES = new ArrayList(5);
static {
- Collections.addAll(PROXY_TEST_LINES, "test1", "test2");
Collections.addAll(PRINTING_TEST_LINES,
"the first test line",
@@ -108,33 +89,6 @@
executor.shutdownNow();
}
- public void testProxy() {
- TestLineProcessor processor1 = new TestLineProcessor(false);
- TestLineProcessor processor2 = new TestLineProcessor(false);
-
- LineProcessor proxy = LineProcessors.proxy(processor1, processor2);
- for (String line : PROXY_TEST_LINES) {
- proxy.processLine(line);
- }
-
- assertEquals(0, processor1.getResetCount());
- assertEquals(0, processor2.getResetCount());
-
- assertEquals(PROXY_TEST_LINES, processor1.getLinesProcessed());
- assertEquals(PROXY_TEST_LINES, processor2.getLinesProcessed());
-
- proxy.reset();
-
- assertEquals(1, processor1.getResetCount());
- assertEquals(1, processor2.getResetCount());
-
- proxy.close();
- assertClosedConditions(proxy);
-
- assertTrue(processor1.isClosed());
- assertTrue(processor2.isClosed());
- }
-
public void testPrinting() {
TestInputWriter writer = new TestInputWriter(new PrintWriter(new ByteArrayOutputStream()));
LineProcessor lineProcessor = LineProcessors.printing(writer, true);
@@ -192,71 +146,6 @@
assertClosedConditions(lineProcessor);
}
- public void testWaiting() throws InterruptedException, BrokenBarrierException {
- final CountDownLatch latch = new CountDownLatch(1);
- final LineProcessor lineProcessor = LineProcessors.patternWaiting(
- Pattern.compile(WAIT_RELEASE_STRING), latch);
- CyclicBarrier barrier = new CyclicBarrier(2);
-
- executor.execute(new WaitRunnable(latch, barrier));
- barrier.await();
- lineProcessor.processLine(WAIT_RELEASE_STRING);
-
- try {
- barrier.await(DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (TimeoutException ex) {
- fail("Deadlock occurs");
- }
-
- executor.execute(new WaitRunnable(latch, barrier));
- barrier.await();
- try {
- barrier.await(DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (TimeoutException ex) {
- fail("Deadlock occurs");
- }
-
- lineProcessor.close();
- assertClosedConditions(lineProcessor);
- }
-
- @RandomlyFails // NB-Core-Build #8029
- public void testWaitingThreadSafety() throws InterruptedException, BrokenBarrierException {
- final CountDownLatch latch = new CountDownLatch(1);
- final LineProcessor lineProcessor = LineProcessors.patternWaiting(
- Pattern.compile(WAIT_RELEASE_STRING), latch);
- CyclicBarrier barrier = new CyclicBarrier(WAIT_THREAD_COUNT + 1);
-
- for (int i = 0; i < WAIT_THREAD_COUNT; i++) {
- executor.execute(new WaitRunnable(latch, barrier));
- }
-
- barrier.await();
-
- Random random = new Random();
- for (int i = 0; i < PRODUCER_THREAD_COUNT; i++) {
- executor.execute(new ProducerRunnable(lineProcessor, WAIT_RELEASE_STRING, random.nextInt(5)));
- }
-
- // guarantee finish
- executor.execute(new Runnable() {
- public void run() {
- try {
- Thread.sleep(TEST_TIMEOUT);
- lineProcessor.processLine(WAIT_RELEASE_STRING);
- } catch (InterruptedException ex) {
- //throw new RuntimeException(ex);
- }
- }
- });
-
- try {
- barrier.await(TEST_TIMEOUT + DEADLOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (TimeoutException ex) {
- fail("Deadlock occurs");
- }
- }
-
private static void assertEquals(List expected, List value) {
assertEquals(expected.size(), value.size());
for (int i = 0; i < expected.size(); i++) {
@@ -279,70 +168,4 @@
// expected
}
}
-
- private static class WaitRunnable implements Runnable {
-
- private final CountDownLatch latch;
-
- private final CyclicBarrier barrier;
-
- public WaitRunnable(CountDownLatch latch, CyclicBarrier barrier) {
- this.latch = latch;
- this.barrier = barrier;
- }
-
- public void run() {
- try {
- barrier.await();
- latch.await();
- barrier.await();
- } catch (InterruptedException ex) {
- // timeouted test
- Thread.currentThread().interrupt();
- } catch (BrokenBarrierException ex) {
- // timeouted test
- }
- }
-
- }
-
- private static class ProducerRunnable implements Runnable {
-
- private final LineProcessor lineProcessor;
-
- private final String releaseString;
-
- private final Random random = new Random();
-
- private final int iterations;
-
- public ProducerRunnable(LineProcessor lineProcessor, String releaseString, int iterations) {
- this.lineProcessor = lineProcessor;
- this.releaseString = releaseString;
- this.iterations = iterations;
- }
-
- public void run() {
- for (int i = 0; i < iterations; i++) {
- if (Thread.interrupted()) {
- return;
- }
-
- int val = random.nextInt(10);
- if (val == 0) {
- lineProcessor.processLine(releaseString);
- return;
- } else {
- lineProcessor.processLine("generated " + val);
- }
-
- try {
- Thread.sleep(random.nextInt(300));
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- }
- }
}
diff --git a/j2ee.weblogic9/nbproject/project.properties b/j2ee.weblogic9/nbproject/project.properties
--- a/j2ee.weblogic9/nbproject/project.properties
+++ b/j2ee.weblogic9/nbproject/project.properties
@@ -43,7 +43,7 @@
#
javac.source=1.6
-spec.version.base=1.38.0
+spec.version.base=1.39.0
test.config.stableBTD.includes=**/*Test.class
test.config.stableBTD.excludes=\
diff --git a/j2ee.weblogic9/nbproject/project.xml b/j2ee.weblogic9/nbproject/project.xml
--- a/j2ee.weblogic9/nbproject/project.xml
+++ b/j2ee.weblogic9/nbproject/project.xml
@@ -109,6 +109,15 @@