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.

Comments:

Hi,
thanks for the great article.
Unfortunately NetBeans would not build my sample-file.

"The module t1 is not a friend of /Applications/NetBeans/NetBeans 6.5.app/Contents/Resources/NetBeans/gsf1/modules/org-netbeans-modules-extexecution.jar
BUILD FAILED (total time: 0 seconds)"

Have you heard of this/an idea how I can handle it?
I run NetBeans' latest mac-version. I'm not quite sure, but there seems to be only major release version 0 of ExtExecAPI.

Thanks!
martin

Posted by martin on December 27, 2008 at 02:04 PM CET #

It looks like I forgot to emphasise that the API has been introduced recently, so it is available in trunk (targeting NetBeans 7.0). For 6.5 it is just a friend (not stable API).

Posted by Petr Hejl on December 27, 2008 at 02:25 PM CET #

Danke, dass ih r so hilfreich wart! Was mann alles mit dieser Code's so machen kann. Mann haette sich daz vor Jahren nicht einmal vorstellen können!

Posted by Ankara Bilgisayar Bilgisayar Satis on February 26, 2009 at 06:17 AM CET #

thanks..

Posted by kelebek on April 26, 2009 at 04:54 AM CEST #

I plan on using this in a java based server. I will try testing it myself, but off the top of your head - Is it possible to use this code without using the netbeans core etc? i.e. using pure Java and only this api from the netbeans api list?
Thanks.
-Jo

Posted by Jo Vark on April 30, 2010 at 11:58 AM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
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