summaryrefslogtreecommitdiff
path: root/samples/socket/KeepSocketOpen.java
diff options
Diffstat (limited to 'samples/socket/KeepSocketOpen.java')
-rw-r--r--samples/socket/KeepSocketOpen.java537
1 files changed, 537 insertions, 0 deletions
diff --git a/samples/socket/KeepSocketOpen.java b/samples/socket/KeepSocketOpen.java
new file mode 100644
index 0000000..51f2543
--- /dev/null
+++ b/samples/socket/KeepSocketOpen.java
@@ -0,0 +1,537 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ *
+ * Copyright (c) 2000,2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Xerces" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.apache.org. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package socket;
+
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Random;
+
+import socket.io.WrappedInputStream;
+import socket.io.WrappedOutputStream;
+
+import org.apache.xerces.parsers.SAXParser;
+
+import org.xml.sax.AttributeList;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * This sample provides a solution to the problem of 1) sending multiple
+ * XML documents over a single socket connection or 2) sending other types
+ * of data after the XML document without closing the socket connection.
+ * <p>
+ * The first situation is a problem because the XML specification does
+ * not allow a document to contain multiple root elements. Therefore a
+ * document stream must end (or at least appear to end) for the XML
+ * parser to accept it as the end of the document.
+ * <p>
+ * The second situation is a problem because the XML parser buffers the
+ * input stream in specified block sizes for performance reasons. This
+ * could cause the parser to accidentally read additional bytes of data
+ * beyond the end of the document. This actually relates to the first
+ * problem if the documents are encoding in two different international
+ * encodings.
+ * <p>
+ * The solution that this sample introduces wraps both the input and
+ * output stream on both ends of the socket. The stream wrappers
+ * introduce a protocol that allows arbitrary length data to be sent
+ * as separate, localized input streams. While the socket stream
+ * remains open, a separate input stream is created to "wrap" an
+ * incoming document and make it appear as if it were a standalone
+ * input stream.
+ * <p>
+ * To use this sample, enter any number of filenames of XML documents
+ * as parameters to the program. For example:
+ * <pre>
+ * java socket.KeepSocketOpen doc1.xml doc2.xml doc3.xml
+ * </pre>
+ * <p>
+ * This program will create a server and client thread that communicate
+ * on a specified port number on the "localhost" address. When the client
+ * connects to the server, the server sends each XML document specified
+ * on the command line to the client in sequence, wrapping each document
+ * in a WrappedOutputStream. The client uses a WrappedInputStream to
+ * read the data and pass it to the parser.
+ * <p>
+ * <strong>Note:</strong> Do not send any XML documents with associated
+ * grammars to the client. In other words, don't send any documents
+ * that contain a DOCTYPE line that references an external DTD because
+ * the client will not be able to resolve the location of the DTD and
+ * an error will be issued by the client.
+ *
+ * @see socket.io.WrappedInputStream
+ * @see socket.io.WrappedOutputStream
+ *
+ * @author Andy Clark, IBM
+ *
+ * @version $Id: KeepSocketOpen.java,v 1.2 2001/08/23 00:35:18 lehors Exp $
+ */
+public class KeepSocketOpen {
+
+ //
+ // MAIN
+ //
+
+ /** Main program entry. */
+ public static void main(String[] argv) throws Exception {
+
+ // constants
+ final int port = 6789;
+
+ // check args
+ if (argv.length == 0) {
+ System.out.println("usage: java socket.KeepSocketOpen file(s)");
+ System.exit(1);
+ }
+
+ // create server and client
+ Server server = new Server(port, argv);
+ Client client = new Client("localhost", port);
+
+ // start it running
+ new Thread(server).start();
+ new Thread(client).start();
+
+ } // main(String[])
+
+ //
+ // Classes
+ //
+
+ /**
+ * Server.
+ *
+ * @author Andy Clark, IBM
+ */
+ public static final class Server
+ extends ServerSocket
+ implements Runnable {
+
+ //
+ // Data
+ //
+
+ /** Files to send. */
+ private String[] fFilenames;
+
+ /** Verbose mode. */
+ private boolean fVerbose;
+
+ /** Buffer. */
+ private byte[] fBuffer;
+
+ //
+ // Constructors
+ //
+
+ /**
+ * Constructs a server on the specified port and with the given
+ * file list in terse mode.
+ */
+ public Server(int port, String[] filenames) throws IOException {
+ this(port, filenames, false);
+ }
+
+ /**
+ * Constructs a server on the specified port and with the given
+ * file list and verbosity.
+ */
+ public Server(int port, String[] filenames, boolean verbose)
+ throws IOException {
+ super(port);
+ System.out.println("Server: Created.");
+ fFilenames = filenames;
+ fVerbose = verbose;
+ //fBuffer = new byte[1024];
+ fBuffer = new byte[4096<<2];
+ } // <init>(int,String[])
+
+ //
+ // Runnable methods
+ //
+
+ /** Runs the server. */
+ public void run() {
+
+ System.out.println("Server: Running.");
+ final Random random = new Random(System.currentTimeMillis());
+ try {
+
+ // accept connection
+ if (fVerbose) System.out.println("Server: Waiting for Client connection...");
+ final Socket clientSocket = accept();
+ final OutputStream clientStream = clientSocket.getOutputStream();
+ System.out.println("Server: Client connected.");
+
+ // send files, one at a time
+ for (int i = 0; i < fFilenames.length; i++) {
+
+ // open file
+ String filename = fFilenames[i];
+ System.out.println("Server: Opening file \""+filename+'"');
+ FileInputStream fileIn = new FileInputStream(filename);
+
+ // wrap stream
+ if (fVerbose) System.out.println("Server: Wrapping output stream.");
+ WrappedOutputStream wrappedOut = new WrappedOutputStream(clientStream);
+
+ // read file, writing to output
+ int total = 0;
+ while (true) {
+
+ // read random amount
+ //int length = (Math.abs(random.nextInt()) % fBuffer.length) + 1;
+ int length = fBuffer.length;
+ if (fVerbose) System.out.println("Server: Attempting to read "+length+" byte(s).");
+ int count = fileIn.read(fBuffer, 0, length);
+ if (count == -1) {
+ if (fVerbose) System.out.println("Server: EOF.");
+ break;
+ }
+ if (fVerbose) System.out.println("Server: Writing "+count+" byte(s) to wrapped output stream.");
+ wrappedOut.write(fBuffer, 0, count);
+ total += count;
+ }
+ System.out.println("Server: Wrote "+total+" byte(s) total.");
+
+ // close stream
+ if (fVerbose) System.out.println("Server: Closing output stream.");
+ wrappedOut.close();
+
+ // close file
+ if (fVerbose) System.out.println("Server: Closing file.");
+ fileIn.close();
+ }
+
+ // close connection to client
+ if (fVerbose) System.out.println("Server: Closing socket.");
+ clientSocket.close();
+
+ }
+ catch (IOException e) {
+ System.out.println("Server ERROR: "+e.getMessage());
+ }
+ System.out.println("Server: Exiting.");
+
+ } // run()
+
+ } // class Server
+
+ /**
+ * Client.
+ *
+ * @author Andy Clark, IBM
+ */
+ public static final class Client
+ extends HandlerBase
+ implements Runnable {
+
+ //
+ // Data
+ //
+
+ /** Socket. */
+ private Socket fServerSocket;
+
+ /** Wrapped input stream. */
+ private WrappedInputStream fWrappedInputStream;
+
+ /** Verbose mode. */
+ private boolean fVerbose;
+
+ /** Buffer. */
+ private byte[] fBuffer;
+
+ /** Parser. */
+ private SAXParser fParser;
+
+ // parse data
+
+ /** Number of elements. */
+ private int fElementCount;
+
+ /** Number of attributes. */
+ private int fAttributeCount;
+
+ /** Number of ignorable whitespace. */
+ private int fIgnorableWhitespaceCount;
+
+ /** Number of characters. */
+ private int fCharactersCount;
+
+ /** Time at start of parse. */
+ private long fTimeBefore;
+
+ //
+ // Constructors
+ //
+
+ /**
+ * Constructs a Client that connects to the given port in terse
+ * output mode.
+ */
+ public Client(String address, int port) throws IOException {
+ this(address, port, false);
+ fParser = new SAXParser();
+ fParser.setDocumentHandler(this);
+ fParser.setErrorHandler(this);
+ }
+
+ /**
+ * Constructs a Client that connects to the given address:port and
+ * with the specified verbosity.
+ */
+ public Client(String address, int port, boolean verbose)
+ throws IOException {
+ System.out.println("Client: Created.");
+ fServerSocket = new Socket(address, port);
+ fVerbose = verbose;
+ fBuffer = new byte[1024];
+ } // <init>(String,int)
+
+ //
+ // Runnable methods
+ //
+
+ /** Runs the client. */
+ public void run() {
+
+ System.out.println("Client: Running.");
+ try {
+ // get input stream
+ final InputStream serverStream = fServerSocket.getInputStream();
+
+ // read files from server
+ while (!Thread.interrupted()) {
+ // wrap input stream
+ if (fVerbose) System.out.println("Client: Wrapping input stream.");
+ fWrappedInputStream = new WrappedInputStream(serverStream);
+ InputStream in = new InputStreamReporter(fWrappedInputStream);
+
+ // parse file
+ if (fVerbose) System.out.println("Client: Parsing XML document.");
+ InputSource source = new InputSource(in);
+ fParser.parse(source);
+ fWrappedInputStream = null;
+
+ // close stream
+ if (fVerbose) System.out.println("Client: Closing input stream.");
+ in.close();
+
+ }
+
+ // close socket
+ if (fVerbose) System.out.println("Client: Closing socket.");
+ fServerSocket.close();
+
+ }
+ catch (EOFException e) {
+ // server closed connection; ignore
+ }
+ catch (Exception e) {
+ System.out.println("Client ERROR: "+e.getMessage());
+ }
+ System.out.println("Client: Exiting.");
+
+ } // run()
+
+ //
+ // DocumentHandler methods
+ //
+
+ /** Start document. */
+ public void startDocument() {
+ fElementCount = 0;
+ fAttributeCount = 0;
+ fIgnorableWhitespaceCount = 0;
+ fCharactersCount = 0;
+ fTimeBefore = System.currentTimeMillis();
+ } // startDocument()
+
+ /** Start element. */
+ public void startElement(String name, AttributeList attrs) {
+ fElementCount++;
+ fAttributeCount += attrs != null ? attrs.getLength() : 0;
+ } // startElement(String,AttributeList)
+
+ /** Ignorable whitespace. */
+ public void ignorableWhitespace(char[] ch, int offset, int length) {
+ fIgnorableWhitespaceCount += length;
+ } // ignorableWhitespace(char[],int,int)
+
+ /** Characters. */
+ public void characters(char[] ch, int offset, int length) {
+ fCharactersCount += length;
+ } // characters(char[],int,int)
+
+ /** End document. */
+ public void endDocument() {
+ long timeAfter = System.currentTimeMillis();
+ System.out.print("Client: ");
+ System.out.print(timeAfter - fTimeBefore);
+ System.out.print(" ms (");
+ System.out.print(fElementCount);
+ System.out.print(" elems, ");
+ System.out.print(fAttributeCount);
+ System.out.print(" attrs, ");
+ System.out.print(fIgnorableWhitespaceCount);
+ System.out.print(" spaces, ");
+ System.out.print(fCharactersCount);
+ System.out.print(" chars)");
+ System.out.println();
+ } // endDocument()
+
+ //
+ // ErrorHandler methods
+ //
+
+ /** Warning. */
+ public void warning(SAXParseException e) throws SAXException {
+ System.out.println("Client: [warning] "+e.getMessage());
+ } // warning(SAXParseException)
+
+ /** Error. */
+ public void error(SAXParseException e) throws SAXException {
+ System.out.println("Client: [error] "+e.getMessage());
+ } // error(SAXParseException)
+
+ /** Fatal error. */
+ public void fatalError(SAXParseException e) throws SAXException {
+ System.out.println("Client: [fatal error] "+e.getMessage());
+ // on fatal error, skip to end of stream and end parse
+ try {
+ fWrappedInputStream.close();
+ }
+ catch (IOException ioe) {
+ // ignore
+ }
+ throw e;
+ } // fatalError(SAXParseException)
+
+ //
+ // Classes
+ //
+
+ /**
+ * This class reports the actual number of bytes read at the
+ * end of "stream".
+ *
+ * @author Andy Clark, IBM
+ */
+ class InputStreamReporter
+ extends FilterInputStream {
+
+ //
+ // Data
+ //
+
+ /** Total bytes read. */
+ private long fTotal;
+
+ //
+ // Constructors
+ //
+
+ /** Constructs a reporter from the specified input stream. */
+ public InputStreamReporter(InputStream stream) {
+ super(stream);
+ } // <init>(InputStream)
+
+ //
+ // InputStream methods
+ //
+
+ /** Reads a single byte. */
+ public int read() throws IOException {
+ int b = super.in.read();
+ if (b == -1) {
+ System.out.println("Client: Read "+fTotal+" byte(s) total.");
+ return -1;
+ }
+ fTotal++;
+ return b;
+ } // read():int
+
+ /** Reads a block of bytes. */
+ public int read(byte[] b, int offset, int length)
+ throws IOException {
+ int count = super.in.read(b, offset, length);
+ if (count == -1) {
+ System.out.println("Client: Read "+fTotal+" byte(s) total.");
+ return -1;
+ }
+ fTotal += count;
+ if (Client.this.fVerbose) System.out.println("Client: Actually read "+count+" byte(s).");
+ return count;
+ } // read(byte[],int,int):int
+
+ } // class InputStreamReporter
+
+ } // class Client
+
+} // class KeepSocketOpen