Named Pipes with Java

In this post, we will discuss using Named Pipes with Java.

Let’s start with a brief overview of Pipes and Named Pipes.

Overview:
On Unix, a pipe is a channel of communication between two processes, also known as ‘interprocess communication’ (IPC). Pipes are unidirectional, meaning that data travels in one direction at one time. Pipes have a read end and a write end. Data written to the write end of the pipe can be read from the read end. One obvious common use of pipes occurs when ‘pipelining‘ commands in a shell context. On Windows, the concept of pipes exist, however the semantics differ substantially. For example, the Win32 API allows you to create a bidirectional pipe using duplex mode.

Below is an example of a pipelined shell command:
> ls -l | grep key | more

The above command connects the stdout pipe of `ls -l` to the stdin pipe of `grep key` and the stdout pipe of `grep key` to the stdin pipe of `more`.

There are two types of Pipes:
1. Pipes. Also known as Anonymous pipes or Unnamed pipes)
2. Named Pipes (on Unix FIFOs)

Pipes and Named Pipes are essentially the same with the exception of how they are created.

Named Pipes and Java:
Unfortunately, Java does not provide a way to create named pipes, instead we must resort to native APIs e.g. CreateNamedPipe on Windows or mkfifo on Unix. Such native APIs can be accessed via Java using frameworks like Java Native Interface (JNI) or Java Native Access (JNA). In general, I prefer JNA to JNI, however there are cases where JNI out performs JNA. Such a discussion is beyond the scope of this post.

Named pipes are accessible through the file system:
On windows pipes exist in a specialized area \\.\pipe\:
e.g.
\\.\pipe\...

On Unix, a pipe can exist anywhere on the file system and exists as a special file.
e.g.

/myapp/dir/mypipe

Named Pipes can be created as follows:
* Created On Unix/Linux command line
> mkfifo myPipe

* Created on Unix/Linux with C
The following code was borrowed here from cs.fedonia.edu.

#include
#include <sys/stat.h>
#include <sys/types.h>
#include

int main()
{
    int fd;
    char * myfifo = "/tmp/myfifo";

    /* create the FIFO (named pipe) */
    mkfifo(myfifo, 0666);

    /* Open the pipe. Just like you open a file */
    fd = open(myfifo, O_WRONLY);

    /* Write to the pipe */
    write(fd, "Hi", sizeof("Hi"));
    close(fd);

    /* remove the FIFO */
    unlink(myfifo);

    return 0;
}

* On Windows, named pipes can only be created via the Win32 API using C++, C#, …
Note that Windows offers bidirectional pipes
CreateNamedPipe

Finally, let’s assume that a process, procA, has created a named pipe. We would like to communicate with procA from Java using IPC, thus we must establish a connection to the named pipe created by procA. The following code shows how we can attach our Java program to this pipe, thus establishing a communication channel between procA and our Java program.
NOTE: for unblocked bidirectional communication on Unix, procA can simply create two named pipes; one dedicated to requests and one for responses. On Windows, we can do the same or create a single bidirectional pipe.

Read/Write Unidirectional Example:
For unidirectional pipes on Windows, we must ensure we do not create the pipe in duplex mode.
Assuming procA, created the named pipe, /path/to/my_request_pipe (Unix) or \\.\pipe\my_request_pipe, we can use the Java IO package to establish a connection to the pipe.

    try {
        // Connect to the named pipe
        RandomAccessFile pipe = new RandomAccessFile(
	        "\\\\.\\pipe\\my_request_pipe", "rw"); // "r" or "w" or for bidirectional (Windows only) "rw"

        String req = "Request text";

        // Write request to the pipe
        pipe.write(req.getBytes());

        // Read response from pipe
        String res = pipe.readLine();

        // Close the pipe
        pipe.close();

        // do something with res

    catch (Exception e) {
        // do something
    }

NOTE:The example above enables read and write i.e. "rw". Remember, "rw" (or a bidirectional pipe) works on Windows only!
To test a unidirectional pipe, set "rw" to "r" or "w" and comment out the pipe.write or pipe.readLine() respectively.

Unblocked Bidirectional Example:
The following example describes bidirectional communication via two pipes. As we know, on Windows, this can be accomplished using a single bidirectional pipe.
In this case, let’s assume process, procA, has created two named pipes; one dedicated to requests and one for responses. Let’s assume the request pipe is named \\.\pipe\my_req_pipe, and the response pipe is \\.\pipe\my_res_pipe.

    String reqPipePath = "\\\\.\\pipe\\my_req_pipe";
    String resPipePath = "\\\\.\\pipe\\my_res_pipe";
    FileOutputStream requestStream = new  FileOutputStream(reqPipePath);
    FileInputStream responseStream = new FileInputStream(resPipePath);
    // At this point we have a reference to both our req and res stream.
    // By Implementing a producer / consumer like scheme,
    // we can manage unblocked bidirection interprocess communication.

Thank you!

You may also like...

Leave a Reply