Saturday Nov 22, 2008

External Execution Support 1/2

New public API was recently introduced in NetBeans. Its name is External Execution Support and it provides the necessary support to execute external program and/or to read the output from process streams (as well as other sources of data). This post focuses on the second and less advanced part of the API usage - reading and processing data from various sources in a convenient way. This is in fact introduction of the basic API concepts. You will see more useful and advanced usages in the next post.

The module sources are located in extexecution directory and in order to use this API you have to add following fragmet into the project.xml of yor module.

<dependency>
    <code-name-base>org.netbeans.modules.extexecution</code-name-base>
    <build-prerequisite/>
    <compile-dependency/>
    <run-dependency>
        <release-version>1</release-version>
        <specification-version>1.12</specification-version>
    </run-dependency>
</dependency>

One purpose of this API (that will be described in this post) is to process growing streams or files (this will became more obvious in the second part). For this reason one important thing to know is that the read cycle does not exit when the stream starts returning EOF. Lets start with an example. We suppose you have a streams as a source of data and you want to parse the lines and print them to system output.

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.InputReader;
import org.netbeans.api.extexecution.input.InputReaders;
import org.netbeans.api.extexecution.input.LineProcessor;

public final class StreamExample1 {
    
    private final ExecutorService service = Executors.newCachedThreadPool();
    
    public void execute(InputStream stream) {
        InputReader reader = InputReaders.forStream(stream, Charset.defaultCharset());
        LineProcessor lineProcessor = new LineProcessor() {
            public void processLine(String line) {
                System.out.println(line);
            }

            public void reset() {
            }

            public void close() {
            }
        };
        InputProcessor inputProcessor = InputProcessors.bridge(lineProcessor);

        InputReaderTask task = InputReaderTask.newTask(reader, inputProcessor);
        service.execute(task);
    }

    public void finish() {
        service.shutdownNow();
    }
}

Now imagine what would you have to do without this API. To start a thread, read characters from the stream, parse the lines (yourself or with help of java.io.BufferedReader) and process them. When you use the API you have it all for free - the only important part is the code saying what to do with parsed lines.

As you see from the sample the abstraction of data source is InputReader. The custom processing is provided as implementation of InputProcessor or LineProcessor depending on your needs (InputProcessor handles characters, not waiting for the whole line). InputReader and InputProcessor are then passed to factory method of InputReaderTask. InputReaderTask implements Runnable and Cancellable so you can pass it to ExecutorService, run in dedicated thread or invoke run method directly. Once the run() method of the InputReaderTask is invoked it is reading the data from the reader processing them with processor until it is cancelled.

The task can be canceled in two ways. You can either interrupt the thread or explicitly call the cancel() method. The latter is useful when you are using the third party code which reacts to interruption in undesirable way (throwing exception, immediately terminating etc.).

To make things easier API also provides several useful factory classes for InputReader, InputProcessor and LineProcessor [put javadoc links here]. If you want to print output to NetBeans' output window you have even greater posibilities. API provides four factory methods for InputProcessor and LineProcessor writing to the output window. Two of these factory methods accept LineConvertor through which you can change the printed lines and register listeners to them. Enough talking; lets see another sample. It's doing the same thing as the first one however it writes lines to the output window and whenever there is a http URL it provides a listener that will open a browser. Too complicated? I don't think so - see it for yourself.

import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.netbeans.api.extexecution.input.InputProcessors;
import org.netbeans.api.extexecution.input.InputReaderTask;
import org.netbeans.api.extexecution.input.InputReader;
import org.netbeans.api.extexecution.input.InputReaders;
import org.netbeans.api.extexecution.input.LineProcessor;
import org.netbeans.api.extexecution.print.LineConvertors;

public final class StreamExample2 {
    
    private final ExecutorService service = Executors.newCachedThreadPool();
    
    public void execute(InputStream stream, InputOutput io) {
        InputReader reader = InputReaders.forStream(stream, Charset.defaultCharset());
        LineProcessor lineProcessor = LineProcessors.printing(io, false,
                LineConvertors.httpUrl());

        InputReaderTask task = InputReaderTask.newTask(reader,
                InputProcessors.bridge(lineProcessor));
        service.execute(task);
    }

    public void finish() {
        service.shutdownNow();
    }
}

Although you can pass only single covertor to printing support and only single processor to task you can use multiple convertors and processors via proxying implementation available as helper factory methods InputProcessors.proxy(InputProcessor...) and LineConvertors.proxy(LineConvertor...).

The API is generally usable, but its main purpose is to be used for continuous processing of stream and files (yes, files are supported as well). In this blogpost I presented just the first (less interesting part) and I introduced some key interfaces. If you would like to use it to support external process execution wait with that for the next post which will introduce the part of the API designed specifically for this.

About

Petr is a NetBeans software engineer responsible for the Groovy & Grails support.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today