Pages

Sunday, October 10, 2010

Java Native Interface

Java Native Interface or JNI is used to define methods in java which will be implemented in C/C++ code. This is very useful to implement some low level functionality which are not supported by Java.
For an example consider that we are creating a java project named “HelloWorldJNI” which has the following structure and location. 
Class Name: HelloWorldJNI
Structure:
HelloWorldJNI
\src
\bin
.settings
.classpath
.project

Package:
test.HelloWorld
Location:
C:\workspace\HelloWorldJNI

Steps in the JNI programming

Step #1: JNI requires the native methods to be declared in java as,
public class HelloWorld{
       public native void displayHelloWorld();
       static{
             System.loadLibrary("HelloWorldDLL");
       }

}
Step #2: compile the class using jdk’s javac compiler as follows,
C:\workspace\HelloWorldJNI>javac.exe src\test\HelloWorld.java
Step #3: Using jdk’s javah.exe create the header file in C/C++ code by invoking following command from the command prompt.
C:\workspace\HelloWorldJNI>javah -jni -o src\test\HelloWorld.h -classpath C:\workspace\HelloWorldJNI\src test.HelloWorldhere “–o” will define the output file name where “–classpath” will define the working path and “test.HelloWorld” will give the input file name.
Step #4: create a Win32 DLL in C/C++ named HelloWorldDLL and copy the DLL file to the C:\workspace\HelloWorldJNI\src location.
Step #5: create a main method in test.TestApp class in java as follows,
public class TestApp{
     public static vois main(String[] arg){
          HelloWorld hd = new HelloWorld();
          hd.displayHelloWorld();
     }
}
Step #6: in the command prompt run the program as
C:\workspace\HelloWorldJNI>java HelloWorldJNI
This entire process is illustrated in the following diagram which was taken from the The JNI Programmer’s Guide. Finally the result will be displayed in the command prompt as “Hello World!” as shown in the below diagram.

A complete JNI learning experience can be gained through going through The Java Native Interface Programmer’s Guide and Specification. Hope this article helped you to do a simple Hello World application correctly which has a packaging structure.


Named Pipes for Inter Process Communication

What is Inter Process Communication (IPC)?

Inter process communication refers to passing of information between processes. These processes can be located either on a single host or on multiple hosts in a network. According to the context in which they operate techniques used for inter process communication should differ to obtain the maximum performance. Given below are some of the popular inter process communication methods.

Different techniques of IPC

Not all operating systems support all IPC techniques. The IPC techniques mentioned below also have different speeds of execution, convenience, reliability levels and security levels. It’s interesting and important to have a good understanding about other alternatives before analyzing one approach of inter process communication in detail. Some of the widely used IPC techniques and the OS support they have are mentioned below,

  • Files:  Supported by all Operating Systems. Very primitive mechanism. Not efficient and highly vulnerable.
  • Message Passing: Used in Java RMI, CORBA, etc. Has lot of limitations.
  • Shared Memory: Supported by all POSIX Systems and Windows Systems. Faster than both Pipes and Sockets.                
  • Sockets: Supported by all Operating Systems. Efficient in server client communication in a network environment.
  • Pipes and Named Pipes: Supported by all POSIX Systems and Windows Systems. Slightly faster than Sockets.

Pipes as an Inter Process Communication Technique

Pipe communication is a effective server/client communication mechanism which enables communication between a server and 1 or more clients. Pipes has 2 forms which has different characteristics. They are,

  1. "Anonymous pipes" (or "Unnamed pipes")
  2. "Named Pipes".

Let’s look at both forms separately. But main focus of this article is to introduce you Named Pipes in detail.

Anonymous Pipes

Anonymous Pipes are used to transfer information between related processes. It should be noted that Anonymous pipes cannot be used to communicate among processes over a network. Unnamed pipes are also unidirectional by nature. Hence to enable bidirectional communication between server and a client, 2 separate pipes need to be created. Normally to satisfy server/client communication between 2 processes, 2 dedicated pipes are created one for reading and one for writing in this scenario. The lifespan of the unnamed pipe is tightly bounds with the lifespan of the process that creates the pipe.

Usage: This is an efficient mechanism to redirect standard inputs or outputs of a child process with its parent process within the same host.

Named Pipes

Named Pipes in the other hand are used to exchange information between related or unrelated processes, within a single host or within multiple hosts over a network. Name of the pipe is assumed to be well known. And any process can access the pipe if it knows the name and has the right privileges. Lifespan of a named pipe is not tightly bound to the lifespan of the process that creates the pipe and hence needed to be explicitly closed at the end of communication. Most important fact about named pipes is that not like anonymous pipes, the same named pipe can act as a server and a client at different occasions. Hence all named pipe peers are equal. 

Named Pipe Example Code

Since pipes behave similar to files, any mechanism used to create or access files can be used for creating or accessing pipes. But we need to keep in mind that the inputs and outputs of a pipe are always byte streams.

There are 4 important methods to implement when using Named pipes for IPC. They are,

  • StartClient
  • ReadPipe
  • WritePipe
  • StopClient

Approach #1 will demonstrate how we can implement a Named Pipe Client which has all these methods and uses 2 separate pipes to establish communication.

public class NamedPipeClient {
    private static InputStream pipeIn;
    private static OutputStream pipeOut;

    public NamedPipeClient() {
           public void StartClient()
    }

    public void StopClient() {
        pipeIn = null;
        pipeOut = null;
    }
    public void StartClient() {
            //initialize the pipe after a small sleep time
            File pipe = new File(“\\\\.\\pipe\\testpipe”);

            try{
                 pipeOut = new FileOutputStream(pipe);
                 pipeIn = new FileInputStream(pipe);
            }catch(FileNotFoundException e){
                 e.printStackTrace();
            }
    }
        public boolean Write(byte[] m_pTxDataBuffer, short totalpktLength) {
        boolean wSuccess = false;
        try {
            pipeOut.write(m_pTxDataBuffer, 0, totalpktLength);
            pipeOut.flush();
            wSuccess = true;
        } catch (NullPointerException e) {
            System.out.println("Exception! Write buffer is null.");
            e.printStackTrace();
        }catch(IndexOutOfBoundsException e){
            System.out.println("Exception! Write length exceeds write buffer size or total packets to write contain a invalid value.");
            e.printStackTrace();
        }catch (IOException e){
            System.out.println("IOException while writing to the pipe.");
            e.printStackTrace();
        }
        return wSuccess;
    }

    public int Read(byte[] pchBuff, int inBufferSize) {
        int byteTransfer = 0;
        byteTransfer = pipeIn.read(pchBuff, 0, inBufferSize);
        } catch (NullPointerException e) {
            System.out.println("Exception! Read buffer is null.");
            e.printStackTrace();
        }catch (IndexOutOfBoundsException e) {
            System.out.println("Exception! Read length exceeds read buffer size.");
            e.printStackTrace();
        }catch (IOException e) {
            System.out.println("IO Exception while reading from the pipe.");
            e.printStackTrace();
        }
        return byteTransfer;
    }
}

Approach #2 will demonstrate how we can implement a simple Named Pipe Client which has all these methods and uses a single bidirectional pipe to establish communication.

public class NamedPipeClient {

    RandomAccessFile rafPipe = null;

    public NamedPipeClient() {
        StartClient();
    }

public void StopClient() {
        try {
            rafPipe.close();
        } catch (IOException e) {
            System.out.println("IOException while closing the pipe streams");
            e.printStackTrace();
        }
        rafPipe = null;
    }

    public void StartClient() {
            //create the pipe
            try {
                rafPipe = new RandomAccessFile( pipeName, "rws");
            } catch (FileNotFoundException e) {
                System.out.println("FileNotFound exception while creating the pipe...");
                e.printStackTrace();
            }
    }
    public boolean write(byte[] m_pTxDataBuffer, short totalpktLength) {
        boolean wSuccess = false;
        try {
            rafPipe.write(m_pTxDataBuffer, 0, totalpktLength);
            wSuccess = true;
        }catch (IOException e){
            System.out.println("IOException while writing to the pipe...");
            e.printStackTrace();
        }
        return wSuccess;
    }

    public int Read(byte[] pchBuff, int inBufferSize) {
        int byteTransfer = 0;
        try {
            byteTransfer = rafPipe.read(pchBuff, 0, inBufferSize);
        }catch (IOException e) {
            System.out.println("IO Exception while reading from the pipe.");
            e.printStackTrace();
        }
        return byteTransfer;
    }
}

Approach #3 will demonstrate how we can improve the Named Pipe Client mentioned in Approach #2 to support non-blocking reads. If explained further, in above example the read function get blocked until at least one byte is read from the pipe. Hence when reading from the pipe is performed continuously from a separate thread, even writes can get blocked when there is nothing to read. For this I have used the length() method available with RandomAccessFile which will provide the pipe data length in a non blocking way. Since StopClient() and StartClient() methods are identical to what is given in Approach #2, I’ll not include them here. In adding to introducing non-blocking read functionality I have introduced locking scheme to prevent deadlocks.

public class NamedPipeClient {

    RandomAccessFile rafPipe = null;
   
static long curLength;
    static boolean writeNotify = false;
    static boolean wrote = false;
    static boolean readNotify = false;

    public boolean write(byte[] m_pTxDataBuffer, short totalpktLength) {
        boolean wSuccess = false;
        if(totalpktLength<0){
            totalpktLength = 0;
        }
        try {
            while(readNotify){}//wait for read to finished
            writeNotify = true;
            rafPipe.write(m_pTxDataBuffer, 0, totalpktLength);
            wrote = true;
        }catch (IOException e){
            System.out.println("IOException while writing to the pipe...");
            e.printStackTrace();
        }finally{
            writeNotify = false;
        }
        return wSuccess;
    }

    public int Read(byte[] pchBuff, int inBufferSize){
        int byteTransfer = -1;
        if(inBufferSize<0){
            inBufferSize = 0;
        }
        try{
            curLength = rafPipe.length();
        }catch(IOException e){
            curLength = -1;
        }
        //handle exceptions
        if(curLength == -1){
            return byteTransfer;
        }
        //handle functionality
        if(!wrote && curLength==0){
            //nothing happened
            return byteTransfer;
        }else if(!wrote && curLength>0){
            //incoming stream. read now
            try{
                while(writeNotify){}//wait for write to finish
                readNotify = true;
                byteTransfer = rafPipe.read(pchBuff, 0, inBufferSize);
            }catch (IOException e) {
                System.out.println("IO Exception while reading from the pipe.");
                e.printStackTrace();
            }finally{
                readNotify = false;
            }
        }else if(wrote && curLength==0){
            //read possible. But not yet available. wait till timeout.
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return byteTransfer;
        }else if(wrote && curLength>0){
            //input stream available. read now
            try{
                while(writeNotify){}//wait for write to finish
                readNotify = true;
                byteTransfer = rafPipe.read(pchBuff, 0, inBufferSize);
            }catch (IOException e) {
                System.out.println("IO Exception while reading from the pipe.");
                e.printStackTrace();
            }finally{
                wrote = false;
                readNotify = false;
            }
        }else{
            //unknown condition
        }
    return byteTransfer;
    }
}

Hope this article helped you to get a better idea on Named Pipes which will help you in your future work..