KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > socket > KeepSocketOpen


1 /*
2  * Copyright 2000,2001,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package socket;
18
19 import java.io.EOFException JavaDoc;
20 import java.io.FileInputStream JavaDoc;
21 import java.io.FilterInputStream JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.net.ServerSocket JavaDoc;
26 import java.net.Socket JavaDoc;
27 import java.util.Random JavaDoc;
28
29 import socket.io.WrappedInputStream;
30 import socket.io.WrappedOutputStream;
31
32 import org.apache.xerces.parsers.SAXParser;
33
34 import org.xml.sax.AttributeList JavaDoc;
35 import org.xml.sax.DocumentHandler JavaDoc;
36 import org.xml.sax.HandlerBase JavaDoc;
37 import org.xml.sax.InputSource JavaDoc;
38 import org.xml.sax.Parser JavaDoc;
39 import org.xml.sax.SAXException JavaDoc;
40 import org.xml.sax.SAXParseException JavaDoc;
41
42 /**
43  * This sample provides a solution to the problem of 1) sending multiple
44  * XML documents over a single socket connection or 2) sending other types
45  * of data after the XML document without closing the socket connection.
46  * <p>
47  * The first situation is a problem because the XML specification does
48  * not allow a document to contain multiple root elements. Therefore a
49  * document stream must end (or at least appear to end) for the XML
50  * parser to accept it as the end of the document.
51  * <p>
52  * The second situation is a problem because the XML parser buffers the
53  * input stream in specified block sizes for performance reasons. This
54  * could cause the parser to accidentally read additional bytes of data
55  * beyond the end of the document. This actually relates to the first
56  * problem if the documents are encoding in two different international
57  * encodings.
58  * <p>
59  * The solution that this sample introduces wraps both the input and
60  * output stream on both ends of the socket. The stream wrappers
61  * introduce a protocol that allows arbitrary length data to be sent
62  * as separate, localized input streams. While the socket stream
63  * remains open, a separate input stream is created to "wrap" an
64  * incoming document and make it appear as if it were a standalone
65  * input stream.
66  * <p>
67  * To use this sample, enter any number of filenames of XML documents
68  * as parameters to the program. For example:
69  * <pre>
70  * java socket.KeepSocketOpen doc1.xml doc2.xml doc3.xml
71  * </pre>
72  * <p>
73  * This program will create a server and client thread that communicate
74  * on a specified port number on the "localhost" address. When the client
75  * connects to the server, the server sends each XML document specified
76  * on the command line to the client in sequence, wrapping each document
77  * in a WrappedOutputStream. The client uses a WrappedInputStream to
78  * read the data and pass it to the parser.
79  * <p>
80  * <strong>Note:</strong> Do not send any XML documents with associated
81  * grammars to the client. In other words, don't send any documents
82  * that contain a DOCTYPE line that references an external DTD because
83  * the client will not be able to resolve the location of the DTD and
84  * an error will be issued by the client.
85  *
86  * @see socket.io.WrappedInputStream
87  * @see socket.io.WrappedOutputStream
88  *
89  * @author Andy Clark, IBM
90  *
91  * @version $Id: KeepSocketOpen.java,v 1.3 2004/02/24 23:41:06 mrglavas Exp $
92  */

93 public class KeepSocketOpen {
94
95     //
96
// MAIN
97
//
98

99     /** Main program entry. */
100     public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
101
102         // constants
103
final int port = 6789;
104
105         // check args
106
if (argv.length == 0) {
107             System.out.println("usage: java socket.KeepSocketOpen file(s)");
108             System.exit(1);
109         }
110
111         // create server and client
112
Server server = new Server(port, argv);
113         Client client = new Client("localhost", port);
114
115         // start it running
116
new Thread JavaDoc(server).start();
117         new Thread JavaDoc(client).start();
118
119     } // main(String[])
120

121     //
122
// Classes
123
//
124

125     /**
126      * Server.
127      *
128      * @author Andy Clark, IBM
129      */

130     public static final class Server
131         extends ServerSocket JavaDoc
132         implements Runnable JavaDoc {
133
134         //
135
// Data
136
//
137

138         /** Files to send. */
139         private String JavaDoc[] fFilenames;
140
141         /** Verbose mode. */
142         private boolean fVerbose;
143
144         /** Buffer. */
145         private byte[] fBuffer;
146
147         //
148
// Constructors
149
//
150

151         /**
152          * Constructs a server on the specified port and with the given
153          * file list in terse mode.
154          */

155         public Server(int port, String JavaDoc[] filenames) throws IOException JavaDoc {
156             this(port, filenames, false);
157         }
158
159         /**
160          * Constructs a server on the specified port and with the given
161          * file list and verbosity.
162          */

163         public Server(int port, String JavaDoc[] filenames, boolean verbose)
164             throws IOException JavaDoc {
165             super(port);
166             System.out.println("Server: Created.");
167             fFilenames = filenames;
168             fVerbose = verbose;
169             //fBuffer = new byte[1024];
170
fBuffer = new byte[4096<<2];
171         } // <init>(int,String[])
172

173         //
174
// Runnable methods
175
//
176

177         /** Runs the server. */
178         public void run() {
179
180             System.out.println("Server: Running.");
181             final Random JavaDoc random = new Random JavaDoc(System.currentTimeMillis());
182             try {
183
184                 // accept connection
185
if (fVerbose) System.out.println("Server: Waiting for Client connection...");
186                 final Socket JavaDoc clientSocket = accept();
187                 final OutputStream JavaDoc clientStream = clientSocket.getOutputStream();
188                 System.out.println("Server: Client connected.");
189
190                 // send files, one at a time
191
for (int i = 0; i < fFilenames.length; i++) {
192
193                     // open file
194
String JavaDoc filename = fFilenames[i];
195                     System.out.println("Server: Opening file \""+filename+'"');
196                     FileInputStream JavaDoc fileIn = new FileInputStream JavaDoc(filename);
197                     
198                     // wrap stream
199
if (fVerbose) System.out.println("Server: Wrapping output stream.");
200                     WrappedOutputStream wrappedOut = new WrappedOutputStream(clientStream);
201                     
202                     // read file, writing to output
203
int total = 0;
204                     while (true) {
205
206                         // read random amount
207
//int length = (Math.abs(random.nextInt()) % fBuffer.length) + 1;
208
int length = fBuffer.length;
209                         if (fVerbose) System.out.println("Server: Attempting to read "+length+" byte(s).");
210                         int count = fileIn.read(fBuffer, 0, length);
211                         if (count == -1) {
212                             if (fVerbose) System.out.println("Server: EOF.");
213                             break;
214                         }
215                         if (fVerbose) System.out.println("Server: Writing "+count+" byte(s) to wrapped output stream.");
216                         wrappedOut.write(fBuffer, 0, count);
217                         total += count;
218                     }
219                     System.out.println("Server: Wrote "+total+" byte(s) total.");
220
221                     // close stream
222
if (fVerbose) System.out.println("Server: Closing output stream.");
223                     wrappedOut.close();
224                     
225                     // close file
226
if (fVerbose) System.out.println("Server: Closing file.");
227                     fileIn.close();
228                 }
229
230                 // close connection to client
231
if (fVerbose) System.out.println("Server: Closing socket.");
232                 clientSocket.close();
233
234             }
235             catch (IOException JavaDoc e) {
236                 System.out.println("Server ERROR: "+e.getMessage());
237             }
238             System.out.println("Server: Exiting.");
239
240         } // run()
241

242     } // class Server
243

244     /**
245      * Client.
246      *
247      * @author Andy Clark, IBM
248      */

249     public static final class Client
250         extends HandlerBase JavaDoc
251         implements Runnable JavaDoc {
252
253         //
254
// Data
255
//
256

257         /** Socket. */
258         private Socket JavaDoc fServerSocket;
259
260         /** Wrapped input stream. */
261         private WrappedInputStream fWrappedInputStream;
262
263         /** Verbose mode. */
264         private boolean fVerbose;
265
266         /** Buffer. */
267         private byte[] fBuffer;
268
269         /** Parser. */
270         private SAXParser fParser;
271
272         // parse data
273

274         /** Number of elements. */
275         private int fElementCount;
276
277         /** Number of attributes. */
278         private int fAttributeCount;
279
280         /** Number of ignorable whitespace. */
281         private int fIgnorableWhitespaceCount;
282
283         /** Number of characters. */
284         private int fCharactersCount;
285
286         /** Time at start of parse. */
287         private long fTimeBefore;
288
289         //
290
// Constructors
291
//
292

293         /**
294          * Constructs a Client that connects to the given port in terse
295          * output mode.
296          */

297         public Client(String JavaDoc address, int port) throws IOException JavaDoc {
298             this(address, port, false);
299             fParser = new SAXParser();
300             fParser.setDocumentHandler(this);
301             fParser.setErrorHandler(this);
302         }
303
304         /**
305          * Constructs a Client that connects to the given address:port and
306          * with the specified verbosity.
307          */

308         public Client(String JavaDoc address, int port, boolean verbose)
309             throws IOException JavaDoc {
310             System.out.println("Client: Created.");
311             fServerSocket = new Socket JavaDoc(address, port);
312             fVerbose = verbose;
313             fBuffer = new byte[1024];
314         } // <init>(String,int)
315

316         //
317
// Runnable methods
318
//
319

320         /** Runs the client. */
321         public void run() {
322
323             System.out.println("Client: Running.");
324             try {
325                 // get input stream
326
final InputStream JavaDoc serverStream = fServerSocket.getInputStream();
327
328                 // read files from server
329
while (!Thread.interrupted()) {
330                     // wrap input stream
331
if (fVerbose) System.out.println("Client: Wrapping input stream.");
332                     fWrappedInputStream = new WrappedInputStream(serverStream);
333                     InputStream JavaDoc in = new InputStreamReporter(fWrappedInputStream);
334
335                     // parse file
336
if (fVerbose) System.out.println("Client: Parsing XML document.");
337                     InputSource JavaDoc source = new InputSource JavaDoc(in);
338                     fParser.parse(source);
339                     fWrappedInputStream = null;
340
341                     // close stream
342
if (fVerbose) System.out.println("Client: Closing input stream.");
343                     in.close();
344
345                 }
346
347                 // close socket
348
if (fVerbose) System.out.println("Client: Closing socket.");
349                 fServerSocket.close();
350
351             }
352             catch (EOFException JavaDoc e) {
353                 // server closed connection; ignore
354
}
355             catch (Exception JavaDoc e) {
356                 System.out.println("Client ERROR: "+e.getMessage());
357             }
358             System.out.println("Client: Exiting.");
359
360         } // run()
361

362         //
363
// DocumentHandler methods
364
//
365

366         /** Start document. */
367         public void startDocument() {
368             fElementCount = 0;
369             fAttributeCount = 0;
370             fIgnorableWhitespaceCount = 0;
371             fCharactersCount = 0;
372             fTimeBefore = System.currentTimeMillis();
373         } // startDocument()
374

375         /** Start element. */
376         public void startElement(String JavaDoc name, AttributeList JavaDoc attrs) {
377             fElementCount++;
378             fAttributeCount += attrs != null ? attrs.getLength() : 0;
379         } // startElement(String,AttributeList)
380

381         /** Ignorable whitespace. */
382         public void ignorableWhitespace(char[] ch, int offset, int length) {
383             fIgnorableWhitespaceCount += length;
384         } // ignorableWhitespace(char[],int,int)
385

386         /** Characters. */
387         public void characters(char[] ch, int offset, int length) {
388             fCharactersCount += length;
389         } // characters(char[],int,int)
390

391         /** End document. */
392         public void endDocument() {
393             long timeAfter = System.currentTimeMillis();
394             System.out.print("Client: ");
395             System.out.print(timeAfter - fTimeBefore);
396             System.out.print(" ms (");
397             System.out.print(fElementCount);
398             System.out.print(" elems, ");
399             System.out.print(fAttributeCount);
400             System.out.print(" attrs, ");
401             System.out.print(fIgnorableWhitespaceCount);
402             System.out.print(" spaces, ");
403             System.out.print(fCharactersCount);
404             System.out.print(" chars)");
405             System.out.println();
406         } // endDocument()
407

408         //
409
// ErrorHandler methods
410
//
411

412         /** Warning. */
413         public void warning(SAXParseException JavaDoc e) throws SAXException JavaDoc {
414             System.out.println("Client: [warning] "+e.getMessage());
415         } // warning(SAXParseException)
416

417         /** Error. */
418         public void error(SAXParseException JavaDoc e) throws SAXException JavaDoc {
419             System.out.println("Client: [error] "+e.getMessage());
420         } // error(SAXParseException)
421

422         /** Fatal error. */
423         public void fatalError(SAXParseException JavaDoc e) throws SAXException JavaDoc {
424             System.out.println("Client: [fatal error] "+e.getMessage());
425             // on fatal error, skip to end of stream and end parse
426
try {
427                 fWrappedInputStream.close();
428             }
429             catch (IOException JavaDoc ioe) {
430                 // ignore
431
}
432             throw e;
433         } // fatalError(SAXParseException)
434

435         //
436
// Classes
437
//
438

439         /**
440          * This class reports the actual number of bytes read at the
441          * end of "stream".
442          *
443          * @author Andy Clark, IBM
444          */

445         class InputStreamReporter
446             extends FilterInputStream JavaDoc {
447
448             //
449
// Data
450
//
451

452             /** Total bytes read. */
453             private long fTotal;
454
455             //
456
// Constructors
457
//
458

459             /** Constructs a reporter from the specified input stream. */
460             public InputStreamReporter(InputStream JavaDoc stream) {
461                 super(stream);
462             } // <init>(InputStream)
463

464             //
465
// InputStream methods
466
//
467

468             /** Reads a single byte. */
469             public int read() throws IOException JavaDoc {
470                 int b = super.in.read();
471                 if (b == -1) {
472                     System.out.println("Client: Read "+fTotal+" byte(s) total.");
473                     return -1;
474                 }
475                 fTotal++;
476                 return b;
477             } // read():int
478

479             /** Reads a block of bytes. */
480             public int read(byte[] b, int offset, int length)
481                 throws IOException JavaDoc {
482                 int count = super.in.read(b, offset, length);
483                 if (count == -1) {
484                     System.out.println("Client: Read "+fTotal+" byte(s) total.");
485                     return -1;
486                 }
487                 fTotal += count;
488                 if (Client.this.fVerbose) System.out.println("Client: Actually read "+count+" byte(s).");
489                 return count;
490             } // read(byte[],int,int):int
491

492         } // class InputStreamReporter
493

494     } // class Client
495

496 } // class KeepSocketOpen
497
Popular Tags