diff --git a/extexecution.base/apichanges.xml b/extexecution.base/apichanges.xml new file mode 100644 --- /dev/null +++ b/extexecution.base/apichanges.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + External Execution Base API + External Execution Base Input API + External Execution Base SPI + + + + + + + + + + Initial version released + + + + + + First initial release of the External Execution Base API. + Refactored from External Execution API. + + + + + + + + + + + Change History for the External Execution Base API + + + + + + +

Introduction

+ +

This document lists changes made to the External Execution Base API.

+ + + +
+ + + +

@FOOTER@

+ + +
+ +
diff --git a/extexecution.base/arch.xml b/extexecution.base/arch.xml new file mode 100644 --- /dev/null +++ b/extexecution.base/arch.xml @@ -0,0 +1,1207 @@ + + + +]> + + + + &api-questions; + + + + +

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

+

+ In order to do so it will implement + + ProcessBuilderImplementation and pass + + ProcessBuilder to its clients. The API instances are created with + help of + ProcessBuilderFactory. +

+
+ +

+ Client wants to destroy the process, trying to kill whole process tree. + Method + + Processes.killTree(java.lang.Process process, Map<String,String> environment) + is designed for that. It will use a + ProcessesImplementation + registered in default lookup to do so. +

+
+
+ + + + + +

+ 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 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). + * + *

+ *

+ * Sample usage (ls command): + *

+ *     BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor()
+ *             .outProcessorFactory(new BaseExecutionDescriptor.InputProcessorFactory() {
+ *
+ *         @Override
+ *         public InputProcessor newInputProcessor() {
+ *             return InputProcessors.copying(new BufferedWriter(new OutputStreamWriter(System.out)));
+ *         }
+ *     });
+ *
+ *     ProcessBuilder processBuilder = ProcessBuilder.getLocal();
+ *     processBuilder.setExecutable(ls);
+ *
+ *     BaseExecutionService service = BaseExecutionService.newService(processBuilder, descriptor);
+ *     Future<Integer> task = service.run();
+ * 
+ *

+ * Even simpler usage but without displaying output (ls command): + *

+ *     ProcessBuilder processBuilder = ProcessBuilder.getLocal();
+ *     processBuilder.setExecutable(ls);
+ *
+ *     ExecutionService service = ExecutionService.newService(processBuilder, new BaseExecutionDescriptor());
+ *     Future<Integer> task = service.run();
+ * 
+ *
+ * + * @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 processCreator; + + private final BaseExecutionDescriptor descriptor; + + public BaseExecutionService(Callable 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 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): + *

+ *     java.lang.Process process = ...
+ *     java.util.concurrent.ExecutorService executorService = ...
+ *     Runnable runnable = InputReaderTask.newTask(
+ *         InputReaders.forStream(process.getInputStream(), Charset.defaultCharset()));
+ *     executorService.submit(runnable);
+ *
+ *     ...
+ *
+ *     executorService.shutdownNow();
+ * 
+ * Sample usage - forwarding data to standard input of the process: + *
+ *     java.lang.Process process = ...
+ *     java.util.concurrent.ExecutorService executorService = ...
+ *     Runnable runnable = InputReaderTask.newTask(
+ *         InputReaders.forStream(System.in, Charset.defaultCharset()),
+ *         InputProcessors.copying(new OutputStreamWriter(process.getOutputStream())));
+ *     executorService.submit(runnable);
+ *
+ *     ...
+ *
+ *     executorService.shutdownNow();
+ * 
+ *
+ * + * @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: + *

    + *
  1. Get table of system environment variables. + *
  2. Put all environment variables configured by + * {@link #addEnvironmentVariable(java.lang.String, java.lang.String)}. + * This rewrites system variables if conflict occurs. + *
  3. 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. + *
  4. 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.

To abstract the source of the data client must implement @@ -214,10 +229,10 @@ The both default printing processors provide factory method accepting LineConvertor. Namely - + InputProcessors.printing(org.openide.windows.OutputWriter out, LineConvertor convertor, boolean resetEnabled) and - + LineProcessors.printing(org.openide.windows.OutputWriter out, LineConvertor convertor, boolean resetEnabled). Convertor is then used to convert received lines to printed ones. @@ -227,8 +242,9 @@

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

- Client wants to destroy the process, trying to kill whole process tree. + Client wants to destroy the process, trying to kill whole process tree. + This usecase should be solved by External Execution Base API. Method ExternalProcessSupport.destroy(java.lang.Process process, Map<String,String> env) @@ -510,9 +527,7 @@ -->

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

@@ -922,6 +937,23 @@ ProcessDestroyPerformer as a tool for killing the whole process tree.

+

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

+

+ The StartupExtenderImplementations + at path StartupExtender are looked up when the API + StartupExtender + needs to know all such extenders. +

diff --git a/extexecution/manifest.mf b/extexecution/manifest.mf --- a/extexecution/manifest.mf +++ b/extexecution/manifest.mf @@ -2,6 +2,6 @@ AutoUpdate-Show-In-Client: false OpenIDE-Module: org.netbeans.modules.extexecution/2 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/extexecution/resources/Bundle.properties -OpenIDE-Module-Specification-Version: 1.42 +OpenIDE-Module-Specification-Version: 1.43 OpenIDE-Module-Recommends: org.netbeans.spi.extexecution.open.OptionOpenHandler, org.netbeans.spi.extexecution.open.FileOpenHandler, org.netbeans.spi.extexecution.open.HttpOpenHandler diff --git a/extexecution/nbproject/project.xml b/extexecution/nbproject/project.xml --- a/extexecution/nbproject/project.xml +++ b/extexecution/nbproject/project.xml @@ -24,6 +24,15 @@ + org.netbeans.modules.extexecution.base + + + + 2 + 1.0 + + + org.openide.filesystems diff --git a/extexecution/src/org/netbeans/api/extexecution/ExecutionDescriptor.java b/extexecution/src/org/netbeans/api/extexecution/ExecutionDescriptor.java --- a/extexecution/src/org/netbeans/api/extexecution/ExecutionDescriptor.java +++ b/extexecution/src/org/netbeans/api/extexecution/ExecutionDescriptor.java @@ -43,6 +43,8 @@ package org.netbeans.api.extexecution; import java.nio.charset.Charset; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.event.ChangeListener; import org.netbeans.api.annotations.common.CheckReturnValue; import org.netbeans.api.annotations.common.NonNull; @@ -53,7 +55,7 @@ import org.openide.windows.InputOutput; /** - * Descriptor for the execution environment. Describes the runtime attributes + * Descriptor for the execution service. Describes the runtime attributes * of the {@link ExecutionService}. *

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

* It will execute the program with an associated I/O window, with stop and @@ -124,12 +114,6 @@ private static final Logger LOGGER = Logger.getLogger(ExecutionService.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(ExecutionService.class.getName(), Integer.MAX_VALUE); - static { // rerun accessor RerunAction.Accessor.setDefault(new RerunAction.Accessor() { @@ -139,21 +123,6 @@ return service.run(required); } }); - - // 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 processCreator; @@ -224,135 +193,66 @@ final OutputWriter err = io.getErr(); final Reader in = io.getIn(); - final CountDownLatch finishedLatch = new CountDownLatch(1); - class ExecutedHolder { private boolean executed = false; } final ExecutedHolder executed = new ExecutedHolder(); - Callable callable = new Callable() { - public Integer call() throws Exception { + BaseExecutionDescriptor realDescriptor = new BaseExecutionDescriptor(); + realDescriptor = realDescriptor.charset(descriptor.getCharset()); + realDescriptor = realDescriptor.inReaderFactory(new BaseExecutionDescriptor.ReaderFactory() { - boolean interrupted = false; - Process process = null; - Integer ret = null; - ExecutorService executor = null; + @Override + public Reader newReader() { + return in; + } + }); + realDescriptor = realDescriptor.preExecution(new Runnable() { - ProcessInputStream outStream = null; - ProcessInputStream errStream = null; + @Override + public void run() { + executed.executed = true; + Runnable orig = descriptor.getPreExecution(); + if (orig != null) { + orig.run(); + } + } + }); + realDescriptor = realDescriptor.postExecution(new ParametrizedRunnable() { - List tasks = new ArrayList(); + @Override + public void run(Integer parameter) { + cleanup(handle, ioData, ioData.getInputOutput() != descriptor.getInputOutput(), + descriptor.isFrontWindowOnError() && parameter != null && parameter != 0); + Runnable orig = descriptor.getPostExecution(); + if (orig != null) { + orig.run(); + } + } + }); + realDescriptor = realDescriptor.outProcessorFactory(new BaseExecutionDescriptor.InputProcessorFactory() { - try { - executed.executed = true; - final Runnable pre = descriptor.getPreExecution(); - if (pre != null) { - pre.run(); - } + @Override + public InputProcessor newInputProcessor() { + return createOutProcessor(out); + } + }); + realDescriptor = realDescriptor.errProcessorFactory(new BaseExecutionDescriptor.InputProcessorFactory() { - 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(descriptor.isInputVisible() ? 3 : 2); - - Charset charset = descriptor.getCharset(); - if (charset == null) { - charset = Charset.defaultCharset(); - } - - tasks.add(InputReaderTask.newDrainingTask( - InputReaders.forStream(new BufferedInputStream(outStream), charset), - createOutProcessor(out))); - tasks.add(InputReaderTask.newDrainingTask( - InputReaders.forStream(new BufferedInputStream(errStream), charset), - createErrProcessor(err))); - if (descriptor.isInputVisible()) { - tasks.add(InputReaderTask.newTask( - InputReaders.forReader(in), - createInProcessor(process.getOutputStream()))); - } - 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 = 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, handle, ioData, - ioData.getInputOutput() != descriptor.getInputOutput(), - descriptor.isFrontWindowOnError() && ret != null && ret.intValue() != 0); - - final Runnable post = descriptor.getPostExecution(); - if (post != null) { - post.run(); - } - } finally { - finishedLatch.countDown(); - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - } - - return ret; + @Override + public InputProcessor newInputProcessor() { + return createErrProcessor(err); } - }; - - final FutureTask current = new FutureTask(callable) { + }); + + BaseExecutionService service = BaseExecutionService.newService(processCreator, realDescriptor); + final Future delegate = service.run(); + + final Future current = new Future() { @Override public boolean cancel(boolean mayInterruptIfRunning) { - boolean ret = super.cancel(mayInterruptIfRunning); + boolean ret = delegate.cancel(mayInterruptIfRunning); if (!executed.executed) { // not executed at all - passing false to show cleanup(handle, ioData, false); @@ -376,14 +276,24 @@ } @Override - protected void setException(Throwable t) { - if (t instanceof WrappedException) { - super.setException(((WrappedException) t).getCause()); - } else { - super.setException(t); - } + public boolean isCancelled() { + return delegate.isCancelled(); } + @Override + public boolean isDone() { + return delegate.isDone(); + } + + @Override + public Integer get() throws InterruptedException, ExecutionException { + return delegate.get(); + } + + @Override + public Integer get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return delegate.get(timeout, unit); + } }; // TODO cleanup @@ -391,6 +301,7 @@ final RerunAction workingRerunAction = ioData.getRerunAction(); Mutex.EVENT.readAccess(new Runnable() { + @Override public void run() { if (workingStopAction != null) { synchronized (workingStopAction) { @@ -413,7 +324,6 @@ cancellable.setTask(current); } - EXECUTOR_SERVICE.execute(current); return current; } @@ -507,30 +417,10 @@ return handle; } - private void cleanup(final List tasks, final ExecutorService processingExecutor, - final ProgressHandle progressHandle, final InputOutputManager.InputOutputData inputOutputData, + private void cleanup(final ProgressHandle progressHandle, + final InputOutputManager.InputOutputData inputOutputData, final boolean managed, final boolean show) { - boolean interrupted = false; - if (processingExecutor != null) { - try { - AccessController.doPrivileged(new PrivilegedAction() { - 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; - } - } - cleanup(progressHandle, inputOutputData, show); synchronized (InputOutputManager.class) { @@ -538,10 +428,6 @@ InputOutputManager.addInputOutput(inputOutputData); } } - - if (interrupted) { - Thread.currentThread().interrupt(); - } } private void cleanup(final ProgressHandle progressHandle, @@ -575,13 +461,19 @@ outProcessor = InputProcessors.bridge(LineProcessors.printing(writer, convertorFactory != null ? convertorFactory.newLineConvertor() : null, true)); } else { - outProcessor = InputProcessors.printing(writer, + outProcessor = org.netbeans.api.extexecution.print.InputProcessors.printing(writer, convertorFactory != null ? convertorFactory.newLineConvertor() : null, true); } InputProcessorFactory descriptorOutFactory = descriptor.getOutProcessorFactory(); if (descriptorOutFactory != null) { - outProcessor = descriptorOutFactory.newInputProcessor(outProcessor); + outProcessor = new BaseInputProcessor(descriptorOutFactory.newInputProcessor( + new DelegatingInputProcessor(outProcessor))); + } else { + InputProcessorFactory2 descriptorOutFactory2 = descriptor.getOutProcessorFactory2(); + if (descriptorOutFactory2 != null) { + outProcessor = descriptorOutFactory2.newInputProcessor(outProcessor); + } } return outProcessor; @@ -594,22 +486,24 @@ errProcessor = InputProcessors.bridge(LineProcessors.printing(writer, convertorFactory != null ? convertorFactory.newLineConvertor() : null, false)); } else { - errProcessor = InputProcessors.printing(writer, + errProcessor = org.netbeans.api.extexecution.print.InputProcessors.printing(writer, convertorFactory != null ? convertorFactory.newLineConvertor() : null, false); } InputProcessorFactory descriptorErrFactory = descriptor.getErrProcessorFactory(); if (descriptorErrFactory != null) { - errProcessor = descriptorErrFactory.newInputProcessor(errProcessor); + errProcessor = new BaseInputProcessor(descriptorErrFactory.newInputProcessor( + new DelegatingInputProcessor(errProcessor))); + } else { + InputProcessorFactory2 descriptorErrFactory2 = descriptor.getErrProcessorFactory2(); + if (descriptorErrFactory2 != null) { + errProcessor = descriptorErrFactory2.newInputProcessor(errProcessor); + } } return errProcessor; } - private InputProcessor createInProcessor(OutputStream os) { - return InputProcessors.copying(new OutputStreamWriter(os)); - } - private static class ProgressCancellable implements Cancellable { private Future task; @@ -622,6 +516,7 @@ this.task = task; } + @Override public synchronized boolean cancel() { if (task != null) { task.cancel(true); @@ -638,16 +533,9 @@ this.io = io; } + @Override public void actionPerformed(ActionEvent e) { io.select(); } } - - private static class WrappedException extends Exception { - - public WrappedException(Throwable cause) { - super(cause); - } - - } } diff --git a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java --- a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java +++ b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java @@ -69,11 +69,10 @@ * Builder handle command, working directory, PATH variable and HTTP proxy. *

* This class is immutable. - *

- * 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 @@ + org.netbeans.modules.extexecution.base + + + + 2 + 1.0 + + + org.netbeans.modules.j2ee.common diff --git a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java --- a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java +++ b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java @@ -49,7 +49,10 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -66,12 +69,12 @@ import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.TargetModuleID; import javax.enterprise.deploy.spi.status.ProgressObject; -import org.netbeans.api.extexecution.ExecutionDescriptor; -import org.netbeans.api.extexecution.ExecutionService; -import org.netbeans.api.extexecution.ExternalProcessBuilder; -import org.netbeans.api.extexecution.input.InputProcessor; -import org.netbeans.api.extexecution.input.InputProcessors; -import org.netbeans.api.extexecution.input.LineProcessor; +import org.netbeans.api.extexecution.base.BaseExecutionDescriptor; +import org.netbeans.api.extexecution.base.BaseExecutionService; +import org.netbeans.api.extexecution.base.input.InputProcessor; +import org.netbeans.api.extexecution.base.input.InputProcessors; +import org.netbeans.api.extexecution.base.input.LineProcessor; +import org.netbeans.api.extexecution.base.input.LineProcessors; import org.netbeans.api.java.platform.JavaPlatform; import org.netbeans.api.java.platform.JavaPlatformManager; import org.netbeans.modules.j2ee.dd.api.application.Application; @@ -88,14 +91,12 @@ import org.netbeans.modules.j2ee.weblogic9.config.WLMessageDestination; import org.netbeans.modules.j2ee.weblogic9.dd.model.WebApplicationModel; import org.netbeans.modules.j2ee.weblogic9.ui.FailedAuthenticationSupport; -import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.filesystems.JarFileSystem; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.openide.util.Utilities; -import org.openide.windows.InputOutput; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -110,8 +111,6 @@ private static final RequestProcessor URL_WAIT_RP = new RequestProcessor("Weblogic URL Wait", 10); // NOI18N - private static final boolean SHOW_CONSOLE = Boolean.getBoolean(CommandBasedDeployer.class.getName() + ".showConsole");; - public CommandBasedDeployer(WLDeploymentManager deploymentManager) { super(deploymentManager); } @@ -151,7 +150,7 @@ LastLineProcessor lineProcessor = new LastLineProcessor(); for (TargetModuleID module : targetModuleID) { String name = module.getModuleID(); - ExecutionService service = createService("-undeploy", lineProcessor, "-name", name); + BaseExecutionService service = createService("-undeploy", lineProcessor, "-name", name); progress.fireProgressEvent(null, new WLDeploymentStatus( ActionType.EXECUTE, CommandType.UNDEPLOY, StateType.RUNNING, NbBundle.getMessage(CommandBasedDeployer.class, "MSG_Undeploying", name))); @@ -219,7 +218,7 @@ LastLineProcessor lineProcessor = new LastLineProcessor(); for (TargetModuleID module : targetModuleID) { String name = module.getModuleID(); - ExecutionService service = createService("-start", lineProcessor, "-name", name); + BaseExecutionService service = createService("-start", lineProcessor, "-name", name); progress.fireProgressEvent(null, new WLDeploymentStatus( ActionType.EXECUTE, CommandType.START, StateType.RUNNING, NbBundle.getMessage(CommandBasedDeployer.class, "MSG_Starting", name))); @@ -288,7 +287,7 @@ LastLineProcessor lineProcessor = new LastLineProcessor(); for (TargetModuleID module : targetModuleID) { String name = module.getModuleID(); - ExecutionService service = createService("-stop", lineProcessor, "-name", name); + BaseExecutionService service = createService("-stop", lineProcessor, "-name", name); progress.fireProgressEvent(null, new WLDeploymentStatus( ActionType.EXECUTE, CommandType.STOP, StateType.RUNNING, NbBundle.getMessage(CommandBasedDeployer.class, "MSG_Stopping", name))); @@ -372,7 +371,7 @@ LOGGER.log(Level.INFO, "Could not deploy {0}", appModule.getName()); continue; } - ExecutionService service = createService("-deploy", lineProcessor, "-name", + BaseExecutionService service = createService("-deploy", lineProcessor, "-name", appModule.getName(), "-upload", appModule.getOrigin().getAbsolutePath()); progress.fireProgressEvent(null, new WLDeploymentStatus( ActionType.EXECUTE, CommandType.START, StateType.RUNNING, @@ -445,7 +444,7 @@ boolean failed = false; LastLineProcessor lineProcessor = new LastLineProcessor(); for (File library : libraries) { - ExecutionService service = createService("-deploy", lineProcessor, + BaseExecutionService service = createService("-deploy", lineProcessor, "-library" , library.getAbsolutePath()); // NOI18N Future result = service.run(); try { @@ -518,7 +517,7 @@ } LastLineProcessor lineProcessor = new LastLineProcessor(); - ExecutionService service = createService("-deploy", lineProcessor, execParams); // NOI18N + BaseExecutionService service = createService("-deploy", lineProcessor, execParams); // NOI18N Future result = service.run(); try { Integer value = result.get(TIMEOUT, TimeUnit.MILLISECONDS); @@ -578,7 +577,7 @@ if (parameters.length > 0) { System.arraycopy(parameters, 0, execParams, 2, parameters.length); } - ExecutionService service = createService("-redeploy", lineProcessor, execParams); // NOI18N + BaseExecutionService service = createService("-redeploy", lineProcessor, execParams); // NOI18N progress.fireProgressEvent(null, new WLDeploymentStatus( ActionType.EXECUTE, CommandType.DISTRIBUTE, StateType.RUNNING, NbBundle.getMessage(CommandBasedDeployer.class, "MSG_Redeploying", name))); @@ -632,7 +631,7 @@ return progress; } - private ExecutionService createService(final String command, + private BaseExecutionService createService(final String command, final LineProcessor processor, String... parameters) { InstanceProperties ip = getDeploymentManager().getInstanceProperties(); @@ -646,41 +645,49 @@ String host = parts[0]; String port = parts.length > 1 ? parts[1] : ""; - ExternalProcessBuilder builder = new ExternalProcessBuilder(getJavaBinary()) - .redirectErrorStream(true); + org.netbeans.api.extexecution.base.ProcessBuilder builder = org.netbeans.api.extexecution.base.ProcessBuilder.getLocal(); + builder.setExecutable(getJavaBinary()); + builder.setRedirectErrorStream(true); + List arguments = new ArrayList(); // NB supports only JDK6+ while WL 9, only JDK 5 if (getDeploymentManager().getDomainVersion() == null || !getDeploymentManager().getDomainVersion().isAboveOrEqual(WLDeploymentFactory.VERSION_10)) { - builder= builder.addArgument("-Dsun.lang.ClassLoader.allowArraySyntax=true"); // NOI18N + arguments.add("-Dsun.lang.ClassLoader.allowArraySyntax=true"); // NOI18N } - builder = builder.addArgument("-cp") // NOI18N - .addArgument(getClassPath()) - .addArgument("weblogic.Deployer") // NOI18N - .addArgument("-adminurl") // NOI18N - .addArgument("t3://" + host + ":" + port) // NOI18N - .addArgument("-username") // NOI18N - .addArgument(username) - .addArgument("-password") // NOI18N - .addArgument(password) - .addArgument(command); + arguments.add("-cp"); // NOI18N + arguments.add(getClassPath()); + arguments.add("weblogic.Deployer"); // NOI18N + arguments.add("-adminurl"); // NOI18N + arguments.add("t3://" + host + ":" + port); // NOI18N + arguments.add("-username"); // NOI18N + arguments.add(username); + arguments.add("-password"); // NOI18N + arguments.add(password); + arguments.add(command); + + arguments.addAll(Arrays.asList(parameters)); + builder.setArguments(arguments); - for (String param : parameters) { - builder = builder.addArgument(param); + final LineProcessor realProcessor; + if (processor != null || LOGGER.isLoggable(Level.FINEST)) { + if (processor == null) { + realProcessor = new LoggingLineProcessor(Level.FINEST); + } else if (!LOGGER.isLoggable(Level.FINEST)) { + realProcessor = processor; + } else { + realProcessor = LineProcessors.proxy(processor, new LoggingLineProcessor(Level.FINEST)); + } + } else { + realProcessor = null; } + BaseExecutionDescriptor descriptor = new BaseExecutionDescriptor().outProcessorFactory(new BaseExecutionDescriptor.InputProcessorFactory() { - ExecutionDescriptor descriptor = new ExecutionDescriptor().inputVisible(true).outLineBased(true); - if (processor != null) { - descriptor = descriptor.outProcessorFactory(new ExecutionDescriptor.InputProcessorFactory() { - - public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { - return InputProcessors.proxy(defaultProcessor, InputProcessors.bridge(processor)); - } - }); - } - if (!SHOW_CONSOLE) { - descriptor = descriptor.inputOutput(InputOutput.NULL); - } - return ExecutionService.newService(builder, descriptor, "weblogic.Deployer " + command); + @Override + public InputProcessor newInputProcessor() { + return InputProcessors.bridge(realProcessor); + } + }); + return BaseExecutionService.newService(builder, descriptor); } private String getClassPath() { @@ -905,7 +912,7 @@ } } - private static class LastLineProcessor implements LineProcessor { + private static class LastLineProcessor implements org.netbeans.api.extexecution.base.input.LineProcessor { private static final Pattern STACK_TRACE_PATTERN = Pattern.compile("^\\s+((at)|(\\.\\.\\.)).*$"); // NOI18N @@ -930,6 +937,28 @@ public void close() { } } + + private static class LoggingLineProcessor implements org.netbeans.api.extexecution.base.input.LineProcessor { + + private final Level level; + + public LoggingLineProcessor(Level level) { + this.level = level; + } + + @Override + public void processLine(String line) { + LOGGER.log(level, line); + } + + @Override + public void reset() { + } + + @Override + public void close() { + } + } private static class ZipEntryInputStream extends InputStream { diff --git a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/optional/WLStartServer.java b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/optional/WLStartServer.java --- a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/optional/WLStartServer.java +++ b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/optional/WLStartServer.java @@ -65,10 +65,10 @@ import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.status.ProgressObject; import org.netbeans.api.extexecution.ExternalProcessBuilder; -import org.netbeans.api.extexecution.ExternalProcessSupport; -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.base.Processes; +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.api.extexecution.startup.StartupExtender; import org.netbeans.modules.j2ee.deployment.plugins.api.CommonServerBridge; import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties; @@ -335,10 +335,10 @@ service.submit(InputReaderTask.newTask(InputReaders.forStream( process.getInputStream(), Charset.defaultCharset()), - InputProcessors.printing(io.getOut(), new ErrorLineConvertor(), true))); + org.netbeans.api.extexecution.print.InputProcessors.printing(io.getOut(), new ErrorLineConvertor(), true))); service.submit(InputReaderTask.newTask(InputReaders.forStream( process.getErrorStream(), Charset.defaultCharset()), - InputProcessors.printing(io.getErr(), new ErrorLineConvertor(), false))); + org.netbeans.api.extexecution.print.InputProcessors.printing(io.getErr(), new ErrorLineConvertor(), false))); } private static void stopService(String uri, final ExecutorService service) { @@ -412,7 +412,7 @@ if (process != null) { Map mark = new HashMap(); mark.put(WLStartTask.KEY_UUID, dm.getUri()); - ExternalProcessSupport.destroy(process, mark); + Processes.killTree(process, mark); } } } @@ -797,7 +797,7 @@ } Map mark = new HashMap(); mark.put(WLStartTask.KEY_UUID, dm.getUri()); - ExternalProcessSupport.destroy(process, mark); + Processes.killTree(process, mark); } while ((System.currentTimeMillis() - start) < TIMEOUT) { @@ -846,7 +846,7 @@ if (stopProcess != null) { Map mark = new HashMap(); mark.put(KEY_UUID, uuid); - ExternalProcessSupport.destroy(stopProcess, mark); + Processes.killTree(stopProcess, mark); stopService(uri, stopService); } } diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -327,6 +327,7 @@ editor.util,\ extbrowser,\ extexecution,\ + extexecution.base,\ extexecution.impl,\ git,\ gototest,\ diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml --- a/nbbuild/javadoctools/links.xml +++ b/nbbuild/javadoctools/links.xml @@ -226,3 +226,4 @@ + diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml --- a/nbbuild/javadoctools/properties.xml +++ b/nbbuild/javadoctools/properties.xml @@ -224,3 +224,4 @@ + diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml --- a/nbbuild/javadoctools/replaces.xml +++ b/nbbuild/javadoctools/replaces.xml @@ -224,3 +224,4 @@ +