In NIO, what is the difference between a mandatory lock and an advisory lock?

A lock makes it possible to lock an entire file or a region of a file. Some filesystems implement advisory locking, which means that programs must all work together in order to preserve file integrity. It’s similar to having a synchronized block, which doesn’t really prevent your data from being corrupted, but just prevents threads to acquire the same lock.
If mandatory locking is implemented in a filesystem, a process cannot access a file or a region on a file in a way that would violate the lock.

What is an NIO Buffer?

A buffer is used to transfer data between channels.

Internal state

Internally, a buffer is an array of data, where the type is determined by the subclass of java.nio.Buffer that is used: ShortBuffer, IntBuffer, FloatBuffer, DoubleBuffer, LongBuffer, CharBuffer and ByteBuffer.

A buffer maintains three essential properties: capacity, limit and position. Look at the example below for how these properties behave.

  • capacity: is the number of elements the buffer can contain. Once a buffer is allocated, its capacity never changes.

  • limit: the index of the first element that should not be read or written. In other words, elements are usable and can be read up to the index limit-1. For writing, the limit is typically equal to the capacity of the Buffer.
  • position: the index of the next element to be read or written. It is never greater than the limit.

    Creating Buffers

    You can’t directly create Buffers using a constructor.

    Example:

       // allocates memory that can contain 128 bytes
       ByteBuffer byteBuffer = ByteBuffer.allocate(128);
       // prints out 128
       System.out.println(byteBuffer.capacity());
     
       // allocates memory that can contain 128 integers
       IntBuffer intBuffer = IntBuffer.allocate(128);
       // prints out 128
       System.out.println(intBuffer.capacity());
    
  • You can also create a view on an existing ByteBuffer. The two buffers, the original and the view will operate on the same memory, so changing in one buffer will have direct effect in the other one.

    Example:

       // allocates memory that can contain 128 bytes
       ByteBuffer byteBuffer = ByteBuffer.allocate(128);
       // prints out 128
       System.out.println(byteBuffer.capacity());
    
       // Create an intBuffer view on the byteBuffer
       IntBuffer intBuffer = byteBuffer.asIntBuffer();
       // prints out 32, because 128 bytes is 32 integers of 4 bytes each
       System.out.println(intBuffer.capacity());
    
  • To create a Buffer from an existing array, use the wrap method. Changing the original array will change the Buffer.

    WrapExample.java:

    import java.nio.*;
     
    public class WrapExample
    {
       public static void main(String []args) {
          long la[] = { 10, 20, 30, 40 };  
          LongBuffer lb = LongBuffer.wrap(la);
     
          System.out.println("LongBuffer before changing original array");
          printBuffer(lb);
     
          // change the original array
          la[2] = 123456;
     
          System.out.println("LongBuffer after changing original array");
          printBuffer(lb);
       }
       
       public static void printBuffer(LongBuffer lb) {
          for (int i=0; i<lb.limit(); i++) {
             System.out.print(lb.get(i) + " ");
          }
          System.out.println();
       }
    }
    

    Reading/writing from and to a Buffer

    To read and write values from a buffer, you use that buffer’s specific get and put methods, that you can use in relative or absolute mode.

    The following example creates a CharBuffer and writes data to it.

    CharBufferTest.java:

    import java.nio.*;
     
    public class CharBufferTest 
    {
       public static void main(String []args) {
          CharBuffer cb = CharBuffer.allocate(10);
     
          cb.put('a'); 	// use relative put
          cb.put(3, 'b');	// use absolute put
          cb.put('c');	// use relative put
    
          printBuffer(cb);
       }
      
       public static void printBuffer(CharBuffer cb) {
          for (int i=0; i<cb.limit(); i++) {
             System.out.print(cb.get(i) + " ");
          }
          System.out.println();
       }
    } 
    

    outputs:

    a c   b
    

    Buffer Operations

    • flip(): Sets the limit to the current position and the position to zero. This method is typically used after a channel-read (put-operations in Buffer terms) and before a channel-write (get-operations in Buffer terms).

    Example buffer:
     
                          position     limit
                             |           |
           +---+---+---+---+---+---+---+
           | 2 | 4 | 3 | 9 | 0 | 0 | 0 |
           +---+---+---+---+---+---+---+
     
    After flip():
     
          position         limit
             |               |
           +---+---+---+---+---+---+---+
           | 2 | 4 | 3 | 9 | 0 | 0 | 0 |
           +---+---+---+---+---+---+---+
     
    Typical usage:
     
       inChannel.read(buffer);      // transfers data from the channel to the buffer
       flip();                      // prepares buffer for relative get-operations
       outChannel.write(buffer);    // transfers data from the buffer to the channel
    
  • clear(): Sets the limit to the Buffer’s capacity and the position to zero. This method is typically used after a channel-write (get-operations in Buffer terms) and before a channel-read (put-operations in Buffer terms).
    Example buffer that was just used by an out channel:
     
                          position
                           limit
                             |
           +---+---+---+---+---+---+---+
           | 2 | 4 | 3 | 9 | 0 | 0 | 0 |
           +---+---+---+---+---+---+---+
     
    After clear():
     
         position                      limit
             |                           |
           +---+---+---+---+---+---+---+
           | 2 | 4 | 3 | 9 | 0 | 0 | 0 |
           +---+---+---+---+---+---+---+
      
    Typical usage:
      
       outChannel.write(buffer);    // transfers data from the buffer to the channel
       clear();                     // prepares buffer for relative put-operations
       inChannel.read(buffer);      // transfers data from the channel to the buffer
    
  • rewind(): Sets the position to zero
  • position(int): with this method, you can assign a new index to the position. If the position is larger than the current limit, an IllegalArgumentException is thrown.
  • limit(int): with this method, you can assign a new index to the limit. The position is set to this limit if it is larger (the position is never larger than the limit).

    Here is an example of the different operations.

    Main.java:

    import java.nio.*;
     
    public class Main
    {
       public static void main(String []args) {
          ByteBuffer bb = ByteBuffer.allocate(10);
     
          printVars("Initial state", bb);
     
          bb.put((byte) 1);
          printVars("bb.put((byte) 1)", bb);
     
          bb.putFloat(3.4f);
          printVars("bb.putFloat(3.4f)", bb);
     
          bb.flip();
          printVars("flip()", bb);
     
          bb.put((byte) 2);
          printVars("bb.put((byte) 2)", bb);
     
          bb.position(5);
          printVars("bb.position(5)", bb);
     
          bb.clear();
          printVars("clear()", bb);
       }
     
       public static void printVars(String state, ByteBuffer bb) {
          System.out.println(state);
          printTag("limit", bb.limit());
          printTag("position", bb.position());
          printLine(bb);
          System.out.print("t| ");
          for (int i=0; i<bb.capacity(); i++) {
             String hexValue = getHexValue(bb, i);
             System.out.print(" " + hexValue.toUpperCase() + " | ");
          }
          System.out.println();
          printLine(bb);
          System.out.println("tCapacity: " + bb.capacity() + 
                             ", remaining: " + bb.remaining());
          System.out.println();
       }
     
       public static void printLine(ByteBuffer bb) {
          System.out.print("t+");
          for (int i=0; i<bb.capacity(); i++) {
             System.out.print("-----+");
          }
          System.out.println();
       }
     
       public static void printTag(String tag, int pos) {
          System.out.print("t   ");
          for (int i=0; i<pos; i++) {
             System.out.print("      ");
          }
          System.out.println(tag);
          System.out.print("t   ");
          for (int i=0; i<pos; i++) {
             System.out.print("      ");
          }
          System.out.println("|");
       }
     
       public static String getHexValue(ByteBuffer bb, int pos) {
          String hexValue = "??";
          if (pos >= bb.limit()) {
             hexValue = "??";
          }
          else {
             hexValue = Integer.toHexString(bb.get(pos));
          }
          if (hexValue.length() > 2) {
             hexValue = hexValue.substring(hexValue.length()-2);
          }
          else if (hexValue.length() == 1) {
             hexValue = "0" + hexValue;
          }
          return hexValue;
       }
    }
    

    outputs:

    Initial state
    	                                                               limit
    	                                                               |
    	   position
    	   |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  00 |  00 |  00 |  00 |  00 |  00 |  00 |  00 |  00 |  00 | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 10
     
    bb.put((byte) 1)
    	                                                               limit
    	                                                               |
    	         position
    	         |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  01 |  00 |  00 |  00 |  00 |  00 |  00 |  00 |  00 |  00 | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 9
     
    bb.putFloat(3.4f)
    	                                                               limit
    	                                                               |
    	                                 position
    	                                 |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  01 |  40 |  59 |  99 |  9A |  00 |  00 |  00 |  00 |  00 | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 5
     
    flip()
    	                                 limit
    	                                 |
    	   position
    	   |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  01 |  40 |  59 |  99 |  9A |  ?? |  ?? |  ?? |  ?? |  ?? | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 5
     
    bb.put((byte) 2)
    	                                 limit
    	                                 |
    	         position
    	         |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  02 |  40 |  59 |  99 |  9A |  ?? |  ?? |  ?? |  ?? |  ?? | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 4
     
    bb.position(5)
    	                                 limit
    	                                 |
    	                                 position
    	                                 |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  02 |  40 |  59 |  99 |  9A |  ?? |  ?? |  ?? |  ?? |  ?? | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 0
     
    clear()
    	                                                               limit
    	                                                               |
    	   position
    	   |
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	|  02 |  40 |  59 |  99 |  9A |  00 |  00 |  00 |  00 |  00 | 
    	+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
    	Capacity: 10, remaining: 10
    

  • Changing the byte order in a ByteBuffer

    The default byte order in a ByteBuffer is ByteOrder.BIG_ENDIAN. The most significant byte is written out first (little endian is the opposite). Change the order as follows.

    Main.java:

    import java.nio.*;
     
    public class Main
    {
       public static void main(String []args) {
          ByteBuffer bb = ByteBuffer.allocate(25);
     
          for (int i=1; i<=5; i++) {
             bb.putShort((short) i);
          }
          
          // the initial order of a byte buffer is always BIG_ENDIAN
          System.out.println("Before changing byteorder: ");
          printByteArray(bb.array());
     
          // from now on, everything is stored as LITTLE_ENDIAN
          bb.order(ByteOrder.LITTLE_ENDIAN);
     
          for (int i=1; i<=5; i++) {
             bb.putShort((short) i);
          }
     
          System.out.println("After changing byteorder: ");
          printByteArray(bb.array());
       }
     
       public static void printByteArray(byte[] array) {
          for (int i=0; i<array.length; i++) {
             System.out.print(array[i] + " ");
          }
          System.out.println();
       }
    }
    

    outputs:

    Before changing byteorder:
    0 1 0 2 0 3 0 4 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    After changing byteorder:
    0 1 0 2 0 3 0 4 0 5 1 0 2 0 3 0 4 0 5 0 0 0 0 0 0
    

    Performance gain of using NIO channels as opposed to regular streams for doing file I/O

    To test things out, I wrote a couple small Java programs to copy a file:

    CopyFile1.java: uses the old approach using BufferedInput/BufferedOutputStreams with the default buffer size of 2048 bytes
    CopyFile2.java: uses the NIO libraries with different buffer sizes
    CopyFile3.java: uses the NIO FileChannel method transferTo

    CopyFile1.java uses BufferedInputStream and BufferedOutputStream. If you look at the source code for these files, the default buffer is an array of 2048 bytes. The first version of the NIO CopyFile2 program is also set to use an internal buffer of 2048 bytes. Other tests have been done with larger buffer sizes. As expected, the more buffer space allocated, the higher the performance (at a cost of more memory usage).

    The result of copying the Java SDK file j2sdk-1_4_0-win.exe(37Meg) using JDK1.2.2, JDK1.3 and JDK1.4 on a Win2000/450Mhz machine is plotted here:


    (All numbers are seconds)

    CopyFile1.java:

    import java.io.*;
     
    public class CopyFile1
    {
       public static void main(String []args) throws IOException {
          if (args.length != 2) {
             System.err.println("Usage: java CopyFile1 source dest");
             System.exit(1);
          }
     
          String source = args[0];
          String dest = args[1];
     
          long start = System.currentTimeMillis();
     
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
     
          int k;
          while ((k = bis.read()) != -1) {
             bos.write(k);
          }
       
          bis.close();
          bos.close();
     
          long stop = System.currentTimeMillis();
     
          System.out.println("Total time it took to copy: " + (stop - start) + "ms.");
       }
    }
    

    CopyFile2.java:

    import java.nio.*;
    import java.nio.channels.*;
    import java.io.*;
     
    public class CopyFile2
    {
       public static void main(String []args) throws IOException {
          if (args.length != 2) {
             System.err.println("Usage: java CopyFile2 source dest");
             System.exit(1);
          }
     
          String source = args[0];
          String dest = args[1];
     
          long start = System.currentTimeMillis();
     
          FileInputStream fis = new FileInputStream(source);
          FileOutputStream fos = new FileOutputStream(dest);
          
          FileChannel channelIn = fis.getChannel();
          FileChannel channelOut = fos.getChannel();
     
          ByteBuffer buffer = ByteBuffer.allocateDirect(2048); 
     
          int n = channelIn.read(buffer);
          while (n > -1) {
             buffer.flip();
             channelOut.write(buffer);
             buffer.clear();
      
             n = channelIn.read(buffer);
          }      
     
          long stop = System.currentTimeMillis();
     
          System.out.println("Total time it took to copy: " + (stop - start) + "ms.");
       }
    }
    

    CopyFile3.java:

    import java.nio.*;    
    import java.nio.channels.*;    
    import java.io.*;        
     
    public class CopyFile3 
    {        
       public static void main(String args[]) throws IOException {                
          if (args.length != 2) {
             System.err.println("Usage: java CopyFile3 source dest");
             System.exit(1);
          }             
     
          String source = args[0];
          String dest = args[1];
     
          long start = System.currentTimeMillis();
     
          FileInputStream fis = new FileInputStream(source);            
          FileOutputStream fos = new FileOutputStream(dest);            
          
          FileChannel channelIn = fis.getChannel();
          FileChannel channelOut = fos.getChannel();
     
          channelIn.transferTo(0, channelIn.size(), channelOut);                
     
          channelIn.close();            
          channelOut.close();            
     
          long stop = System.currentTimeMillis();
     
          System.out.println("Total time it took to copy: " + (stop - start) + "ms.");
       }    
    }
    

    Getting the list of network interfaces installed on your machine

    If you have multiple IP addresses on one machine, it may be useful to specify which one to use for your networking stuff. Since JDK1.4, you can enumerate them and select one with the class java.net.NetworkInterface.

    Main.java:

    import java.util.*;
    import java.net.*;
     
    public class Main
    {
       public static void main(String []args) {
          try {
             Enumeration enum = NetworkInterface.getNetworkInterfaces();
             while (enum.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) enum.nextElement();
                System.out.println(ni);
             }
          }
          catch(SocketException e) {
             e.printStackTrace();
          }
       }
    }
    

    outputs on my Win2000 machine:

    name:lan0 (Intel DC21140 PCI Fast Ethernet Adapter) index: 1 addresses:
    /192.168.2.10;
    
    name:lan1 (Intel DC21140 PCI Fast Ethernet Adapter) index: 2 addresses:
    
    name:lo0 (MS TCP Loopback interface) index: 3 addresses:
    /127.0.0.1;
    

    Allowing for multiple selections in a JTree

    Use TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION!

    Main.java:

    import javax.swing.*;
    import javax.swing.tree.*;
    import javax.swing.event.*;
    import java.awt.*;
    import java.net.*;
    import java.awt.event.*;
     
    public class Main extends JFrame
    {
       public Main() {
          DefaultMutableTreeNode root = createNodes();
          final JTree tree = new JTree(root);
     
          TreeSelectionModel model = new DefaultTreeSelectionModel();
          model.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
          tree.setSelectionModel(model);
     
          JButton button = new JButton("Display selected values on stdout");
          button.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent ae) {
                TreePath[] treePaths = tree.getSelectionPaths();    
                for (int i=0; i<treePaths.length; i++) {
                   System.out.println(treePaths[i].getLastPathComponent());    
                }
             }
          });
     
          getContentPane().setLayout(new BorderLayout());
          getContentPane().add(BorderLayout.CENTER, new JScrollPane(tree));
          getContentPane().add(BorderLayout.SOUTH, button);
     
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
       }
     
       public static DefaultMutableTreeNode createNodes() {
          DefaultMutableTreeNode root = new DefaultMutableTreeNode("Java");
          
          DefaultMutableTreeNode j2se = new DefaultMutableTreeNode("J2SE");
          DefaultMutableTreeNode j2ee = new DefaultMutableTreeNode("J2EE");
          DefaultMutableTreeNode j2me = new DefaultMutableTreeNode("J2ME");
     
          j2se.add(new DefaultMutableTreeNode("http://java.sun.com/j2se/"));
          j2ee.add(new DefaultMutableTreeNode("http://java.sun.com/j2ee/"));
          j2me.add(new DefaultMutableTreeNode("http://java.sun.com/j2me/"));
     
          root.add(j2se);
          root.add(j2ee);
          root.add(j2me);
     
          return root;
       }
     
       public static void main(String []args) {
          Main main = new Main();
          main.setSize(400, 400);
          main.setVisible(true);
       }
    }
    

    Preventing a JTree to be expanded or collapsed

    Use the TreeWillExpandListener interface!

    The following example allows expanding of nodes, but prevents collapsing.

    Main.java:

    import javax.swing.*;
    import javax.swing.tree.*;
    import javax.swing.event.*;
    import java.awt.*;
    import java.net.*;
    import java.awt.event.*;
     
    public class Main extends JFrame
    {
       public Main() {
          DefaultMutableTreeNode root = createNodes();
          JTree tree = new JTree(root);
     
          tree.addTreeWillExpandListener(new TreeWillExpandListener() {
             public void treeWillCollapse(TreeExpansionEvent event) 
                                throws ExpandVetoException 
             {
                // prevent collapsing
                throw new ExpandVetoException(event);
     
             }
             public void treeWillExpand(TreeExpansionEvent event) 
                                throws ExpandVetoException 
             {
                // do nothing
             }
          });
     
          getContentPane().add(new JScrollPane(tree));
     
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
       }
     
       public static DefaultMutableTreeNode createNodes() {
          DefaultMutableTreeNode root = new DefaultMutableTreeNode("Java");
          
          DefaultMutableTreeNode j2se = new DefaultMutableTreeNode("J2SE");
          DefaultMutableTreeNode j2ee = new DefaultMutableTreeNode("J2EE");
          DefaultMutableTreeNode j2me = new DefaultMutableTreeNode("J2ME");
     
          j2se.add(new DefaultMutableTreeNode("http://java.sun.com/j2se/"));
          j2ee.add(new DefaultMutableTreeNode("http://java.sun.com/j2ee/"));
          j2me.add(new DefaultMutableTreeNode("http://java.sun.com/j2me/"));
     
          root.add(j2se);
          root.add(j2ee);
          root.add(j2me);
     
          return root;
       }
     
       public static void main(String []args) {
          Main main = new Main();
          main.setSize(400, 400);
          main.setVisible(true);
       }
    }
    

    Having a JTextArea as a node of a JTree

    Main.java:

    import javax.swing.border.*;
    import javax.swing.event.*;
    import javax.swing.tree.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
    import java.net.*;
     
    public class Main extends JFrame
    {
       public Main() {
          DefaultMutableTreeNode root = createNodes();
          JTree tree = new JTree(root);
          //tree.setCellEditor(new DefaultCellEditor(new JTextField())); 
          tree.setCellRenderer(new DelegateDefaultCellRenderer());
          tree.setRowHeight(-1);
     
          getContentPane().add(new JScrollPane(tree));
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
       }
     
       public static DefaultMutableTreeNode createNodes() {
          DefaultMutableTreeNode root = new DefaultMutableTreeNode(&amp;amp;quot;Java&amp;amp;quot;);
          
          DefaultMutableTreeNode j2se = new DefaultMutableTreeNode(&amp;amp;quot;J2SE&amp;amp;quot;);
          DefaultMutableTreeNode j2ee = new DefaultMutableTreeNode(&amp;amp;quot;J2EE&amp;amp;quot;);
          DefaultMutableTreeNode j2me = new DefaultMutableTreeNode(&amp;amp;quot;J2ME&amp;amp;quot;);
     
          j2se.add(new DefaultMutableTreeNode(&amp;amp;quot;http://java.sun.com/j2se/&amp;amp;quot;));
          j2ee.add(new DefaultMutableTreeNode(&amp;amp;quot;http://java.sun.com/j2ee/&amp;amp;quot;));
          j2me.add(new DefaultMutableTreeNode(&amp;amp;quot;http://java.sun.com/j2me/&amp;amp;quot;));
     
          root.add(j2se);
          root.add(j2ee);
          root.add(j2me);
     
          return root;
       }
     
       public static void main(String []args) {
          Main main = new Main();
          main.setSize(400, 400);
          main.setVisible(true);
       }
    }
     
    class DelegateDefaultCellRenderer extends DefaultTreeCellRenderer 
    {
       TextAreaRenderer taRenderer = new TextAreaRenderer();
     
       public DelegateDefaultCellRenderer() {
          taRenderer.setBackgroundNonSelectionColor(getBackgroundNonSelectionColor());
          taRenderer.setBackgroundSelectionColor(getBackgroundSelectionColor());
          taRenderer.setTextNonSelectionColor(getTextNonSelectionColor());  
          taRenderer.setTextSelectionColor(getTextSelectionColor()); 
       }
     
       public Component getTreeCellRendererComponent(JTree tree, Object value,
                                      boolean selected, boolean expanded, boolean leaf,
                                      int row, boolean hasFocus)
       {
          if (!leaf) {
             return super.getTreeCellRendererComponent(tree, value, selected, 
                                                       expanded, leaf, row, hasFocus);
          }
          else {
             return taRenderer.getTreeCellRendererComponent(tree, value, selected, 
                                                            expanded, leaf, row, hasFocus);
          }
       }
    }   
     
    class TextAreaRenderer extends JScrollPane implements TreeCellRenderer
    {
       JTextArea textarea;
       Color backgroundNonSelectionColor;
       Color backgroundSelectionColor;
       Color textNonSelectionColor;
       Color textSelectionColor;
     
       public TextAreaRenderer() {
          textarea = new JTextArea(3, 40);
          textarea.setLineWrap(true);
          textarea.setWrapStyleWord(true);
          textarea.setBorder(new TitledBorder(&amp;amp;quot;This is a JTextArea&amp;amp;quot;));
          getViewport().add(textarea);
       }
     
       public void setBackgroundNonSelectionColor(Color c) {
          this.backgroundNonSelectionColor = c;
       }
     
       public void setBackgroundSelectionColor(Color c) {
          this.backgroundSelectionColor = c;
       }
      
       public void setTextNonSelectionColor(Color c) {
          this.textNonSelectionColor = c;
       }
      
       public void setTextSelectionColor(Color c) {
          this.textSelectionColor = c;
       }
     
       public Component getTreeCellRendererComponent(JTree tree, Object value,
                                      boolean selected, boolean expanded, boolean leaf,
                                      int row, boolean hasFocus)
       {
          if (selected) {
             setForeground(textSelectionColor);
             setBackground(backgroundSelectionColor);
             textarea.setForeground(textSelectionColor);
             textarea.setBackground(backgroundSelectionColor);
          } else {
             setForeground(textNonSelectionColor);
             setBackground(backgroundNonSelectionColor);
             textarea.setForeground(textNonSelectionColor);
             textarea.setBackground(backgroundNonSelectionColor);
          }
     
          textarea.setText(&amp;amp;quot;&amp;amp;quot;+((DefaultMutableTreeNode) value).getUserObject()); 
          textarea.setCaretPosition(0);
     
          return this;
       }
    }
    

    The “buffer” and “autoFlush” attributes in the JSP page directive

    Anything generated by the JSP page is stored in a buffer. When the buffer is full, it’s sent back to the client (browser). Sometimes, it is necessary to tweak the buffer and autoFlush attributes to give you more control over how and when the buffer is flushed to the browser.

    For example, if you’re not careful about a response.sendRedirect, you may get an java.lang.IllegalStateException. This occurs when you’re performing a redirect after data has already been committed to the browser.

    The autoFlush attribute tells the JSP engine when the buffer should be flushed. By default it is set to true. If you set it to false and the buffer becomes full, an exception will be thrown.

    When the buffer is flushed once, redirection or forwarding won’t work. All changes to the HTTP response header must occur the first time a buffer is sent to the client. For example, be careful where you do cookie processing!

    Examples:

    Redirecting when buffer not flushed


    By default the buffer is 8kb big. The following example tries asks the browser to redirect to another JSP page. Less than 8kb of bytes were written, so the buffer was not flushed. Redirection succeeds.

    DefaultBufferNotFlushed.jsp:

    <html>
    <body>
    <% for (int i=0; i<7000; i++) { %>0<% } %>
    
    <% 
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    Redirecting when buffer already flushed


    By default, the buffer is 8kb big. The following examples writes more than 8kb of data and redirectiong will fail. A java.lang.IllegalStateException will be thrown.

    DefaultBufferFlushed.jsp:

    <html>
    <body>
    <% for (int i=0; i<9000; i++) { %>0<% } %>
    
    <% 
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    Setting buffer=none and autoFlush=false


    Can’t do that!

    AutoFlushFalse.jsp:

    <%@ page buffer="none" autoFlush="false" %>
    <html>
    <body>
    <% for (int i=0; i<9000; i++) { %>0<% } %>
    
    <%
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    The following exception will be thrown:

       Page directive: Illegal combination of buffer="none" && autoFlush="false"
    

    Setting large buffer and default autoFlush (true)


    In the following example, the buffer is set to 50kb. Even though lots of data is written, the buffer doesn’t get filled up before doing a redirect. It succeeds.

    LargeBuffer.jsp:

    <%@ page buffer="50kb" %>
    <html>
    <body>
    <% for (int i=0; i<40000; i++) { %>0<% } %>
    
    <%
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    Setting autoFlush to false and filling up the buffer


    The default buffer is 8kb. The following example sets autoFlush to false and writes more than 8kb to the buffer. The result is a java.io.IOException: Error: JSP Buffer overflow.

    Overflow.jsp:

    <%@ page autoFlush="false" %>
    <html>
    <body>
    <% for (int i=0; i<9000; i++) { %>0<% } %>
    
    <%
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    Setting autoFlush to false and manually flushing


    This example sets the autoFlush attribute to false and manually flushes the buffer every so often. No overflow exception is thrown, but the redirect won’t work because the buffer has been flushed.

    ManualFlushing.jsp:

    <%@ page autoFlush="false" %>
    <html>
    <body>
    <% for (int i=0; i<9000; i++) { %>
    0
    <% 
          if (i % 1000 == 0) {
             out.flush();
          }
       } 
    %>
    
    <%
       try {
          response.sendRedirect("/docs/ResultingPage.jsp");
       }
       catch(Exception e) {
          e.printStackTrace();
       }
    %>
    </body>
    </html>
    

    Check out this link for the tag syntax:
    http://java.sun.com/products/jsp/tags/11/syntaxref11.fm7.html