Network Protocol Java

/*
 * Copyright (c) 2004 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 3nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose,
 * including teaching and use in open-source projects.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book, 
 * please visit http://www.davidflanagan.com/javaexamples3.
 */
//package je3.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.Set;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.Attribute;
/**
 * PrintServiceWebInterface: A simple HTTP server that displays information
 * about all accessible printers on the network.
 */
public class PrintServiceWebInterface {
  public static void main(String[] args) throws IOException {
    // Get the character encoders and decoders we'll need
    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    // The HTTP headers we send back to the client are fixed
    String headers = "HTTP/1.1 200 OK\r\n" + "Content-type: text/html\r\n"
        + "Connection: close\r\n" + "\r\n";
    // We'll use two buffers in our response. One holds the fixed
    // headers, and the other holds the variable body of the response.
    ByteBuffer[] buffers = new ByteBuffer[2];
    buffers[0] = encoder.encode(CharBuffer.wrap(headers));
    ByteBuffer body = ByteBuffer.allocateDirect(16 * 1024);
    buffers[1] = body;
    // Find all available PrintService objects to describe
    PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
    // All of the channels we use in this code will be in non-blocking
    // mode. So we create a Selector object that will block while
    // monitoring all of the channels and will only stop blocking when
    // one or more of the channels is ready for I/O of some sort.
    Selector selector = Selector.open();
    // Create a new ServerSocketChannel, and bind it to port 8000.
    // Note that we have to do this using the underlying ServerSocket.
    ServerSocketChannel server = ServerSocketChannel.open();
    server.socket().bind(new java.net.InetSocketAddress(8000));
    // Put the ServerSocketChannel into non-blocking mode
    server.configureBlocking(false);
    // Now register the channel with the Selector. The SelectionKey
    // represents the registration of this channel with this Selector.
    SelectionKey serverkey = server.register(selector, SelectionKey.OP_ACCEPT);
    for (;;) { // The main server loop. The server runs forever.
      // This call blocks until there is activity on one of the
      // registered channels. This is the key method in non-blocking I/O.
      selector.select();
      // Get a java.util.Set containing the SelectionKey objects for
      // all channels that are ready for I/O.
      Set keys = selector.selectedKeys();
      // Use a java.util.Iterator to loop through the selected keys
      for (Iterator i = keys.iterator(); i.hasNext();) {
        // Get the next SelectionKey in the set, and then remove it
        // from the set. It must be removed explicitly, or it will
        // be returned again by the next call to select().
        SelectionKey key = (SelectionKey) i.next();
        i.remove();
        // Check whether this key is the SelectionKey we got when
        // we registered the ServerSocketChannel.
        if (key == serverkey) {
          // Activity on the ServerSocketChannel means a client
          // is trying to connect to the server.
          if (key.isAcceptable()) {
            // Accept the client connection, and obtain a
            // SocketChannel to communicate with the client.
            SocketChannel client = server.accept();
            // Make sure we actually got a connection
            if (client == null)
              continue;
            // Put the client channel in non-blocking mode.
            client.configureBlocking(false);
            // Now register the client channel with the Selector,
            // specifying that we'd like to know when there is
            // data ready to read on the channel.
            SelectionKey clientkey = client.register(selector, SelectionKey.OP_READ);
          }
        } else {
          // If the key we got from the Set of keys is not the
          // ServerSocketChannel key, then it must be a key
          // representing one of the client connections.
          // Get the channel from the key.
          SocketChannel client = (SocketChannel) key.channel();
          // If we got here, it should mean that there is data to
          // be read from the channel, but we double-check here.
          if (!key.isReadable())
            continue;
          // Now read bytes from the client. We assume that
          // we get all the client's bytes in one read operation
          client.read(body);
          // The data we read should be some kind of HTTP GET
          // request. We don't bother checking it however since
          // there is only one page of data we know how to return.
          body.clear();
          // Build an HTML document as our reponse.
          // The body of the document contains PrintService details
          StringBuffer response = new StringBuffer();
          response.append("Printer Status"
              + "

Printer Status

");
          for (int s = 0; s < services.length; s++) {
            PrintService service = services[s];
            response.append("

").append(service.getName()).append("

");
            Attribute[] attrs = service.getAttributes().toArray();
            for (int a = 0; a < attrs.length; a++) {
              Attribute attr = attrs[a];
              response.append("");
            }
            response.append("
").append(attr.getName()).append("").append(attr)
                  .append("
");
          }
          response.append("\r\n");
          // Encode the response into the body ByteBuffer
          encoder.reset();
          encoder.encode(CharBuffer.wrap(response), body, true);
          encoder.flush(body);
          body.flip(); // Prepare the body buffer to be drained
          // While there are bytes left to write
          while (body.hasRemaining()) {
            // Write both header and body buffers
            client.write(buffers);
          }
          buffers[0].flip(); // Prepare header buffer for next write
          body.clear(); // Prepare body buffer for next read
          // Once we've sent our response, we have no more interest
          // in the client channel or its SelectionKey
          client.close(); // Close the channel.
          key.cancel(); // Tell Selector to stop monitoring it.
        }
      }
    }
  }
}