package ca.szc.keratin.core.net.mbassador;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import net.engio.mbassy.IPublicationErrorHandler;
import net.engio.mbassy.bus.BusConfiguration;
import net.engio.mbassy.bus.MBassador;
import net.engio.mbassy.bus.MessagePublication;
import net.engio.mbassy.listener.Handler;
import org.pmw.tinylog.Logger;
import ca.szc.keratin.core.event.BusWatchdogEvent;
import ca.szc.keratin.core.event.IrcEvent;
/**
* Wraps a MBassador object, so it can be replaced when it breaks without leaving stale references. Uses a watch-dog
* thread to detect when message delivery stops working. Automatically replaces the broken MBassador when this occurs.
*/
public class MBassadorWrapper
{
private MBassador<IrcEvent> delegate;
private final BusConfiguration configuration;
private final Set<Object> listeners;
private final Set<IPublicationErrorHandler> errorHandlers;
public MBassadorWrapper( BusConfiguration configuration )
{
this.configuration = configuration;
listeners = new HashSet<Object>();
errorHandlers = new HashSet<IPublicationErrorHandler>();
rebootDelegate();
Thread wd = new Thread()
{
private Semaphore activityMonitor = new Semaphore( 0 );
private Semaphore watchdogMonitor = new Semaphore( 0 );
public void run()
{
Logger.trace( "Bus watchdog running" );
Thread.currentThread().setName( "BusWatchdog" );
while ( !Thread.interrupted() )
{
try
{
boolean activityTimedout = !activityMonitor.tryAcquire( 30, TimeUnit.SECONDS );
if ( activityTimedout )
{
Logger.trace( "No activity, checking if the bus is alive" );
publish( new BusWatchdogEvent( null ) );
boolean watchdogTimedout = !activityMonitor.tryAcquire( 5, TimeUnit.SECONDS );
if ( watchdogTimedout )
{
Logger.trace( "The bus is not alive, rebooting the bus" );
rebootDelegate();
}
else
{
Logger.trace( "The bus is alive" );
}
}
}
catch ( InterruptedException e )
{
break;
}
}
Logger.trace( "Interrupted, exiting" );
};
@Handler
public void activityMonitor( IrcEvent event )
{
activityMonitor.release();
}
@Handler
public void watchdogMonitor( BusWatchdogEvent event )
{
watchdogMonitor.release();
}
};
subscribe( wd );
wd.start();
}
private synchronized void rebootDelegate()
{
// Make sure old delegate is disposed of
if ( delegate != null )
delegate.shutdown();
// New delegate
delegate = new MBassador<IrcEvent>( configuration );
// Add the active listeners and handler the old delegate had.
for ( Object listener : listeners )
delegate.subscribe( listener );
for ( IPublicationErrorHandler handler : errorHandlers )
delegate.addErrorHandler( handler );
}
public synchronized void addErrorHandler( IPublicationErrorHandler handler )
{
delegate.addErrorHandler( handler );
}
public synchronized void publish( IrcEvent message )
{
delegate.publish( message );
}
public synchronized MessagePublication publishAsync( IrcEvent message )
{
return delegate.publishAsync( message );
}
public synchronized void shutdown()
{
delegate.shutdown();
}
public synchronized void subscribe( Object listener )
{
delegate.subscribe( listener );
listeners.add( listener );
}
public synchronized boolean unsubscribe( Object listener )
{
listeners.remove( listener );
return delegate.unsubscribe( listener );
}
}