1. "How does a container detect that a component is broken?" Whether that broken-ness results from a worker thread crashing, or from an unhandled exception in a synchronous call is irrelevant.
+1. But I'm not so sure we're only looking at container doing the detection.....there'd be various parties interested in this information. So we'll also be looking at a way for querying the container to find out what components are broken. Not yet, though.
2. "How does a container recover from a broken component?"
Dynamic reloading may not always work - consider components
that maintain state between method calls (SAXTransformer).
Sometimes, a component should just be marked "broken" and
any calls to it blocked to keep it from hurting itself.
I am guessing several 'recovery' policies need to exist. Encoding just one of them into core contracts seems a bad idea......
public interface Monitorable {
public void setMonitor (ComponentMonitor monitor);
}
If I replace "able" with "Aware" in your example I end up with an XWork-style ("IoC type 3") setup. To problem with that (as many have found out by now) is the proliferation of XXXable interfaces, that it is difficult to remove the 'XXX' functionality in subclasses (like so adequately described wrt the Cloneable interface in various books), etc etc.
What about
public service( ServiceManager sm )
{
monitor = sm.lookup( ComponentMonitor.ROLE );
}? Seems more consistent with avalon semantics.
public interface ComponentMonitor {
public void statusChange (Status status);
}
the objection someone raised to this one is that it breaks IoC because it is a way for a component to tell the container to do something. The obvious way around that is to make the monitor active and have it use polling, ie in my example it was:
/** m_monitor runs a seperate thread which polls for status */ m_monitor.monitor( runnable, runner, m_component );
Which is probably less efficient though...nor does it scale...any thoughts on that? In general, it seems to be there's a class of problems which are very hard to solve using IoC but simpler using events/callbacks/ listeners/etc. Is this a good place to break IoC? Why? Can we identify a pattern here?
public class Status {
public static final Status OK = new Status ("ok"); public static final Status BROKEN = new Status ("broken");
}
it seems the 'status' should be a customizable enumeration to allow for application-specific policies. Perhaps something similar to HTTP (class of 1xx, 2xx, 3xx, 4xx error messages allowing a few base policies, and more specific policies based on the 'xx' in advanced implementations and/or [5-9]xx) could be figured out. Maybe using subclassing...
public class Status {}
public class StatusOK extends Status {}
public class StatusBroken extends Status {}
public class StatusDeadWorkerThread extends StatusBroken {}...wait a minute...seems that's just recreating a hierarchy to parallel the exception hierarchy...maybe
public class Status
{
private Throwable m_problem = null; public Status( Throwable problem ) { m_problem = problem; }
public Throwable getProblem( m_problem ) { return m_problem; }
public boolean isBroken() { return m_problem != null; }
}is flexible but still simple?
[the above decomposition] can solve [the issues mentioned above]?
Yeah, seems like it could work, and it looks simple enough, and it might even be possible to implement using lifecycle extensions.
======= Summary ======= We're now looking at this seperation of concerns:
1 - work execution 2 - status monitoring
(1) can be addressed naturally using an Executor, whether or not thread management comes into the picture is not a concern of the client component. In fact, we've made all thread management policy configurable in a client-transparent way :D
The case you make is that (2) might be addressed best using a passive, one-instance-per-component, status-based component monitor, with the main argument that such a setup is much simpler than any kind of polling. I'll add that its also likely to be more efficient.
There's an open question: why is there a neccessary and sufficient set of conditions for deviating from the usual approach of applying strict IoC? (what are those conditions and why do they apply here?)
======================== Complete picture in code ========================
Work execution -------------- Re-use from util.concurrent:
interface Executor
{
void execute( Runnable runnable );
}Monitoring ---------- Passive and event-based:
public interface ComponentMonitor
{
public void statusChange( Status status );
}
public class Status
{
private Throwable m_problem = null; public Status( Throwable problem ) { m_problem = problem; }
public Throwable getProblem( m_problem ) { return m_problem; }
public boolean isBroken() { return m_problem != null; }
}Socket server example (code sketch)
-----------------------------------
/** Listens for socket requests and handles them. */
interface SocketServer {}
/** Deals with a single socket request at a time. */
interface ConnectionHandler
{
public void handle( Socket socket );
}/** @avalon.component type="SocketServer" */
class ThreadedSocketServer implements SocketServer,
Servicable, Configurable, Initializable, Startable, Disposable
{
public final static int DEFAULT_PORT = 80;
public final static int DEFAULT_BACK_LOG = 100;
public final static String DEFAULT_ADDRESS = "localhost";
private int m_port;
private int m_backlog;
private String m_address;private ServerSocket m_socket; private Monitor m_monitor;
private Worker m_worker; private ConnectionHandler m_handler;
public void configure( Configuration conf )
throws ConfigurationException
{
m_port = conf.getChild("port").getValueAsInteger( DEFAULT_PORT );
m_backlog = conf.getChild("backlog")
.getValueAsInteger( DEFAULT_BACKLOG );
m_address = conf.getChild("address").getValue( DEFAULT_ADDRESS );
} /**
* @avalon.dependency type="Executor"
* @avalon.dependency type="ConnectionHandler"
* @avalon.dependency type="Monitor"
*/
public void service( ServiceManager sm ) throws ServiceException
{
m_executor = sm.Lookup( Executor.ROLE );
m_handler = sm.lookup( ConnectionHandler.ROLE );
m_monitor = sm.lookup( Monitor.ROLE );
} public void initialize() throws Exception
{
m_socket = getNewServerSocket();
m_worker = new Worker();
} public void start() throws Exception
{
m_executor.execute( m_worker );
} public void stop()
{
m_worker.stop();
} public void dispose()
{
try { m_socket.close(); }
catch( IOException ioe ) {}
} protected void getNewServerSocket()
{
InetAddress address = InetAddress.getByName( m_address );
ServerSocket socket =
new ServerSocket( m_port, m_backlog, m_address );return socket; }
private class Worker implements Runnable
{
private boolean running = false; public void stop()
{
running = false;
}
public void run()
{
running = true; while(running)
{
if(Thread.isInterrupted())
{
running = false;
break; // die
} try
{
Socket socket = m_socket.accept(); // block
m_handler.handle( socket ); // delegate
}
catch( Throwable t )
{
m_monitor.statusChange( new Status( e ) );
// notify others
}
}
}
}
}Observations
------------
- it is not transparent to the component that its being monitored. It'd be nice if it were. Not sure whether that's even remotely feasible for 'generic' monitoring unless we introduce some magic (in the form of AOP).
- the configure()/service()/start()/stop()/initialize()/dispose() do not fire status changes to the monitor....assumed is that the monitor is container-provided and that these status changes will be sent to the monitor, if neccessary, by the container.
- I found several bugs and there's likely to be more; this code will likely not compile :D
- interesting problem, this is :D
cheers,
- LSD
PS: http://www.google.com/search?q=java+thread+monitoring reveals all this is an area of active research :D
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
