Reputation: 60564
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
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