Tomas Aschan
Tomas Aschan

Reputation: 60564

Reading XML from Socket streams

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?

Upvotes: 2

Views: 4167

Answers (1)

Tomas Aschan
Tomas Aschan

Reputation: 60564

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.

Upvotes: 2

Related Questions