package org.prevayler.foundation.network;
import java.io.IOException;

/** 
 * Stubborn Network Proxies are bound to each Service instance in 
 * a Prevayler providing Server services for Replication.
 * This allows the binding to be dynamically connected to a Client
 * replicator, thereby facilitating reconnection after failure.
 * A failed receive causes the proxy to capture the thread until
 * the replication client (network) reconnects.
 * The Proxy never invokes "close" on the service layer.
 */
public class StubbornNetworkProxy implements ObjectReceiver {
  private ObjectReceiver _network;
  private ObjectReceiver _client;
  private StubbornNetworkClientConnector _inboundChannel;
  public StubbornNetworkProxy(){
  }
  protected synchronized void connect(  ObjectReceiver network,  StubbornNetworkClientConnector inboundChannel){
    _network=network;
    _inboundChannel=inboundChannel;
    openClientChannel();
  }
  protected synchronized void disconnect(){
    _inboundChannel=null;
    _network=null;
  }
  protected void setClient(  ObjectReceiver client){
    _client=client;
  }
  public ObjectReceiver getClient(){
    return _client;
  }
  public void receive(  Object object){
    if (isOpen()) {
      attemptToForward(object);
    }
 else {
      waitTillOpen();
      attemptToForward(object);
    }
  }
  private synchronized boolean forwarded(  Object object){
    try {
      if (_network == null) {
        return false;
      }
      _network.receive(object);
      return true;
    }
 catch (    IOException handle) {
      return false;
    }
  }
  private void attemptToForward(  Object object){
    while (!forwarded(object)) {
      closeClientChannel();
      waitTillOpen();
    }
  }
  private synchronized void waitTillOpen(){
    try {
      wait();
    }
 catch (    InterruptedException unexpected) {
    }
  }
  private synchronized boolean isOpen(){
    return (_network != null);
  }
  public void close() throws IOException {
    closeClientChannel();
    _client=null;
  }
  private synchronized void openClientChannel(){
    notify();
  }
  private void closeClientChannel(){
    try {
      if (_inboundChannel != null)       _inboundChannel.disconnect();
      if (_network != null)       _network.close();
    }
 catch (    IOException ignorable) {
    }
 finally {
      _network=null;
      _inboundChannel=null;
    }
  }
}
