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;
private static int OPEN=1;
private static int CONNECTED=2;
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 {
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) {
}
finally {
_sessionState=CLOSED;
}
}
public void receive( Object object) throws IOException {
waitIfNotOpen();
forwardToProvider(object);
}
private synchronized void waitIfNotOpen(){
try {
if (!sessionIsConnected()) {
wait();
}
}
catch ( InterruptedException unEx) {
}
}
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) {
}
}
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);
}
}
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);
}
}
}
}