2

I have a socket connection over which I wish to send XML messages, using the DOM classes (DocumentBuilder et al) to construct and parse the XML documents, and I can't figure out a good way to handle closing the connection while waiting for messages.

The messages may arrive in any order (i.e. I don't know which end will send the next message) and I'd like to avoid any solution based on sleep() for blocking - I want to learn how to do it better than that. I've tried a mutlitude of variants of the following setup:

class XML {
    static Message readMessage(InputStream s) {
       // try/catch and setup of builder omitted for brevity

       Document doc = builder.parse(s);

       // Traverse the doc tree to built the Message, then return
    }

    static void writeMessage(Message m, OutputStream s) {
        // Build a Document from the provided message

        Transformer transformer = tf.newTransformer();
        transformer.transform(new DOMSource(doc), new StreamResult(output));
        output.write('\n');
    }
}

class Connection {
    Socket s;
    Thread reader;

    Connection(Socket s) {
        this.s = s;

        reader = new Thread(new Runnable() {
            void run() {
                while (true) {
                    Message m = XML.readMessage(s.getInputStream());

                    // Handle the message here, e.g. by dispatching UI events
                }
            }
        });
        reader.start();
    }

    void sendMessage(Message msg) {
        XML.writeMessage(msg, s.getOutputStream());
    }

    void close() {
        if (reader.isAlive()) {
            reader.interrupt();
        }
        if (!socket.isClosed()) {
            socket.close();
        }
    }
}

I can get a server and a client to connect to each other OK and start waiting for messages, by simply instantiating a Connection on each side with the respective Socket. Sending and receiving messages also work fine.

However, I can't figure out a way to close the connection cleanly. I keep running into various exceptions. The exact message depends on the implementation details in the skeleton above, but as far as I can understand they all arise when the reading thread tries to read a message after the socket is closed, or the socket closing while the reading thread is still reading. I always close the connection using the close() method above.

Since I'm just passing the stream to the DOM library, I don't have fine-grained control of exactly how it is read.

How do I correctly end the connection, without causing exceptions in the reader thread?

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • Are you wanting to open a connection and have a single request/response or are you wanting to have multiple requests/responses on the same connection? – Brett Okken Jun 07 '14 at 13:49
  • @BrettOkken: I'm keeping the connection open for the duration of the "conversation", so I'm expecting multiple messages sent from both sides, in no particular order, until one side sends a special "disconnect" message and closes the connection. At that point, both sides should close their sockets and exit. – Tomas Aschan Jun 07 '14 at 14:16
  • Then you are likely going to have to define a protocol that wraps the xml to clearly define the beginning and ending of individual xml documents. You then need to wrap the socket input stream and simulate an end of file (i.e. input stream read returns -1) when parsing xml. Likewise and the sending side you need to wrap the output stream and send this special marker to indicate end of message. – Brett Okken Jun 07 '14 at 14:35
  • In your `close()` method, after interrupting the `reader` thread, call `reader.join()`. That should give it a chance to exit cleanly and cease all operations, provided you are not just ignoring IOExceptions in your loop. (Usually the best way to deal with an I/O loop in Java is to have the try/catch *outside* of the loop, so the loop will automatically terminate when encountering an error.) – VGR Jun 07 '14 at 14:36
  • You could also have a look to this other post so SO, focusing on "graceful shutdown" : [Java Sockets and Dropped Connections](http://stackoverflow.com/questions/2028620/java-sockets-and-dropped-connections). Maybe it is enough to get rid of errors. – Serge Ballesta Jun 07 '14 at 14:53

1 Answers1

2

I managed to produce something that actually works, even if I cringe at how ugly this solution is. This is what I did (and since it works, I'll post it as an answer):

InputStreamReader reader = new InputStreamReader(stream);
BufferedReader breader = new BufferedReader(reader);
String xml = breader.readLine();
InputStream newstream = new ByteArrayInputStream(xml.getBytes(Charset.forName("UTF-8")));

Document doc = builder.parse(newstream);

That is, I had to instantiate four extra objects, ending up with an object of exactly the same type as the one I started with, just to make the parser realize that it has the entire document.

If there is a nicer way to do this, I'll gladly accept and upvote any answer that describes it.

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • I have answered a similar question [here](http://stackoverflow.com/a/23361290/3080094). I think it provides a cleaner solution than this answer. – vanOekel Jun 10 '14 at 18:45