import java.io.*;
import java.net.*;
/**
* Title: Safe Object Streams
* Copyright: Copyright (c) 2004
* Company: Superliminal Software
* @author Melinda Green
* @version 1.0
*
* Description:
* A helper object that safely creates pairs of ObjectInput/OutputStream
* objects. This requires a tricky bit of client/server handshaking
* to set up. The purpose of this class is to make this a safe and easy process.
* Note: when used on one end of a Socket, the other end must also.
*
* The reason this is tricky is
* because threads will easily deadlock whenever an ObjectInputStream is constructed
* before the corresponding ObjectOutputStream is created at the other end
* of the socket. one difficult question is how the client and server can signal
* each other over their shared socket that they've created their
* ObjectOutputStreams without corrupting the socket's ability to send objects.
* That is handled that by using PushbackInputStreams to perform blocking reads
* to wait for the other's signal, and then unreading the data and then using
* the pristine PushbackInputStream as the ObjectInputStream's data source.
*/
public class SOS {
private ObjectOutputStream m_oos;
private InputStream m_is;
private ObjectInputStream m_ois=null;
/**
* Creates a Safe Object Streams object.
* @param soc one end of a socket to transmit objects.
* note: the other end of the socket must be similarly treated.
*/
public SOS(Socket soc) {
try {
OutputStream os = soc.getOutputStream();
m_oos = new ObjectOutputStream(os);
m_oos.flush();
InputStream is = soc.getInputStream();
PushbackInputStream pis = new PushbackInputStream(is);
int got = pis.read(); // blocking read in order to wait until ok-to-read signal received from other end
pis.unread(got);
m_is = pis;
}
catch(Exception e) {
System.err.println("SOS exception " + e);
}
// should now be safe to construct ObjectInputStream without risk of deadlock
}
public ObjectOutputStream getOutputStream() {
return m_oos;
}
public ObjectInputStream getInputStream() {
// it must now be safe to construct object input stream because constructor must have
// unblocked after it connected to the other end.
if(m_ois != null)
return m_ois;
try {
m_ois = new ObjectInputStream(m_is);
}
catch(Exception e) {
System.err.println("SOS exception " + e);
}
return m_ois;
}
//
// EVERYTHING FROM HERE DOWN IS TEST CODE
//
private static final int TEST_PORT = 7777;
private static void startServer() {
try {
final ServerSocket ss = new ServerSocket(TEST_PORT);
new Thread() {
public void run() {
while(true) {
try {
SOS sos = new SOS(ss.accept());
ObjectInputStream ois = sos.getInputStream();
Object obj = ois.readObject();
System.out.println("SOS test server read obj from SOS: " + obj);
}
catch(Exception e) {
System.err.println("server exception " + e);
}
}
}
}.start();
}
catch(Exception e) {
System.err.println("server exception " + e);
}
}
/**
* the simplest possible example client/server program that tests SOS objects.
*/
public static void main(String args[]) {
Object to_send = "THIS IS A MESSAGE FROM CLIENT TO SERVER"; // could be any serializable object
try {
startServer();
Socket s = new Socket("localhost", TEST_PORT);
System.out.println("cliet writing to SOS: " + to_send);
SOS sos = new SOS(s);
sos.getOutputStream().writeObject(to_send);
}
catch(Exception e) {
System.err.println("client exception " + e);
}
}
}