package org.dcache.services.info.conduits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple, abstract implementation of a blocking InfoConduit. A new Thread
* is created on enable(), this calls the abstract method activity() which is
* presumed to block (e.g., for incoming network connections).
* <p>
* A further abstract method triggerActivityToReturn() allows the thread to
* escape from activity(), so the thread can be shut down cleanly.
*
* @author Paul Millar
*/
abstract class AbstractThreadedConduit implements Runnable, Conduit
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractThreadedConduit.class);
private Thread _thd;
protected int _callCount;
volatile boolean _should_run=true;
/**
* Start conduit activity.
*/
@Override
public void enable()
{
_callCount = 0;
_should_run = true;
if (_thd == null) {
_thd = new Thread(this, getClass().getSimpleName() + " conduit");
_thd.start();
LOGGER.info("Thread {} started", _thd.getName());
} else {
LOGGER.error("Request to start when thread is already running.");
}
}
/**
* Stop all conduit activity.
*/
@Override
public void disable()
{
if (_thd == null) {
return;
}
_should_run = false;
LOGGER.trace("Signalling thread {} to stop", _thd.getName());
triggerBlockingActivityToReturn();
LOGGER.trace("Waiting for thread to finish...");
try {
_thd.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
_thd = null;
}
@Override
public boolean isEnabled()
{
return _thd != null;
}
/**
* Typically, activity() will include some element that blocks.
* This method should break that blocking call and cause the activity()
* method to return quickly.
*/
abstract void triggerBlockingActivityToReturn();
/**
* A method provides some activity; typically, this method
* will block, pending network activity.
*/
abstract void blockingActivity();
/**
* This class's private thread. Simply loop over the
* (subclass-specific) blocking activity.
*/
@Override
public void run()
{
while (_should_run) {
blockingActivity();
}
LOGGER.info("Thread {} stopped", _thd.getName());
}
/**
* Since we anticipate each conduit to have only one instance, we return the Class simple
* name here.
*/
@Override
public String toString()
{
return this.getClass().getSimpleName();
}
/**
* Return some metadata about this conduit.
*/
@Override
public String getInfo()
{
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(isEnabled() ? "enabled" : "disabled");
if (isEnabled()) {
sb.append(", ");
sb.append(_callCount);
}
sb.append("]");
return sb.toString();
}
}