/*
* StubbornClientReceiverImpl.java
*
*
*/
package org.prevayler.foundation.network;
import java.io.IOException;
import org.prevayler.foundation.Cool;
/**
* The StubbronClientReceiver is an ObjectReceiver to the Client and has
* a connection to the Socket (Provider).
*
* It uses its being in the middle of the exchange to implement a recovery
* mechanism on session failure.
*
* At startup the Replication Client, sends its current session identifier
* to the Replication Server, which if it matches a prior session, it
* reconnects the session. It sends a new session identifier.
*
* If the client sends a zero session identifier it assumes it is a new
* connection and replies with a new session identifier to use in subsequent
* connections.
*/
public class StubbornClientReceiverImpl extends Thread implements ObjectReceiver {
private static int CLOSED = 0; /* the connection is closed */
private static int OPEN = 1; /* open but hasn't exchanged token */
private static int CONNECTED = 2; /* have exchanged token */
private ObjectReceiver _client;
private ObjectSocket _socketProvider;
private StubbornNetwork _stubbornNetwork;
private String _ipAddress;
private int _port;
private volatile boolean _wantedOpen;
private NetworkSessionId _sessionKey;
private int _sessionState;
public StubbornClientReceiverImpl(StubbornNetwork stubbornNetwork,
String ipAddress,
int port,
ObjectReceiver client) {
_stubbornNetwork = stubbornNetwork;
_ipAddress = ipAddress;
_port = port;
_client = client;
_wantedOpen = true;
_sessionState = CLOSED;
_sessionKey = new NetworkSessionId(0,0);
super.setName("Prevayler Stubborn Client " + port);
super.setDaemon(true);
super.start();
}
public StubbornClientReceiverImpl(ObjectSocket socket, Service service ) {
}
/**
* Wait for the connection to occur before proceeding
*/
protected void networkRequestToReceive(Object object) throws IOException {
if (sessionIsConnected()) {
_client.receive(object);
} else { // must be the session id token
receiveSessionKey(object);
wakeUpSleepingClient();
}
}
private void receiveSessionKey (Object sessionKey) {
_sessionKey = (NetworkSessionId)sessionKey;
}
private synchronized void wakeUpSleepingClient() {
_sessionState = CONNECTED;
notify();
}
private void closeForReconnect() {
try {
_socketProvider.close();
} catch (IOException ignore) {
// Do Nothing
} finally {
_sessionState = CLOSED;
}
}
public void receive(Object object) throws IOException {
waitIfNotOpen();
forwardToProvider(object);
}
private synchronized void waitIfNotOpen() {
try {
if (!sessionIsConnected()) {
wait();
}
} catch (InterruptedException unEx) {
//Uh-oh
}
}
private void forwardToProvider(Object object) {
try {
_socketProvider.writeObject(object);
} catch (IOException unExpected) {
shutdown();
waitToResend(object);
}
}
private synchronized void waitToResend(Object object) {
try {
wait();
forwardToProvider(object);
} catch (InterruptedException uhOh) {
//TODO: ????
}
}
public void close() throws IOException {
_wantedOpen = false;
shutdown();
}
private void shutdown() {
if (sessionIsOpen()) {
closeForReconnect();
}
}
private boolean sessionIsOpen() {
return (_sessionState > CLOSED);
}
private boolean sessionIsConnected() {
return (_sessionState == CONNECTED);
}
public void run () {
while (_wantedOpen)
try {
_socketProvider = _stubbornNetwork.newInstance(_ipAddress, _port);
sendSessionKey();
startReading();
} catch (IOException iox) {
closeForReconnect();
Cool.sleep(1000); /* try again every second */
}
}
private void sendSessionKey() throws IOException {
_socketProvider.writeObject(_sessionKey);
_sessionState = OPEN;
}
private void startReading() throws IOException{
while (_wantedOpen) {
try {
Object o = _socketProvider.readObject();
networkRequestToReceive(o);
} catch (ClassNotFoundException returnIt) {
_socketProvider.writeObject(returnIt);
}
}
}
}