2010/12/15 - Apache Excalibur has been retired.

For more information, please explore the Attic.

Setting Up The Command Manager

Using Command is a two step process. You have to set it up, and then you can send Commands to it. Because Command uses an Event Pipeline to move the Commands through the Queue to the EventHandler, we need to set up a ThreadManager. Currently the only ThreadManager that works as advertized is the TPCThreadManager. TPC stands for "Thread Per CPU". The TPCThreadManager allows you to customize its behaviour by passing in some parameters. The code snippet below is fairly typical:

                
ThreadManager threadManager = new TPCThreadManager();
threadManager.enableLogging( getLogger().getChildLogger("threadmanager") );
Parameters params = new Parameters();
params.setParameter( "threads-per-processor", "2" );
params.setParameter( "sleep-time", "1000" );
params.setParameter( "block-timeout", "250" );
threadManager.parameterize( params );
threadManager.initialize();
      
            

We create a Threadmanager, pass in the Logger, pass in the Parameters, and then initialize it. The table below provides all the parameter names that TPCThreadManager recognizes:

Name Description Default Value
processors Number of processors (autodetected if less than one) Results from SystemUtil.numProcessors()
threads-per-processor Threads per processor to use (Rewritten to 1 if less than one) 1
sleep-time Time (in milliseconds) to wait between queue pipeline processing runs 1000
block-timeout Time (in milliseconds) to wait for a thread to process a pipeline 1000

Once the ThreadManager is set up and used, we can set up the CommandManager. We do this by instantiating the CommandManager, and registering it with the ThreadManager. Below is a code snippet showing how that is done:

                
// Create the CommandManager
CommandManager commandManager = new CommandManager();

// Register it with the ThreadManager
threadManager.register( commandManager );
              
            

Running Commands

There are three Command interfaces: Command, DelayedCommand, and RepeatedCommand. Each one of those has a special purpose. The Command interface exposes the method that the CommandManager will execute named, oddly enough, "execute()". The Delayed Command is used to specify a number of milliseconds to wait before the command is run. That delay is based on when the CommandManager receives the DelayedCommand, not on when the object was created. Lastly the RepeatedCommand interface is used to determine how long and how many times the Command will be executed. Below is a code snippet showing how to send Commands to the CommandManager:

                
Sink commandSink = commandManager.getCommandSink();
commandSink.enqueue( new MySpecialCommand() );
              
            

It's not that hard to use the CommandManager. Writing a Command is as easy as implementing the java.lang.Runnable interface. There are two distinct advantages to using the Command infrastructure: your Command can throw exceptions and it won't cause anything to break, and you have the ability to automatically have your Command run again. Just keep in mind that the Command infrastructure was meant for short lived management functions happening in the background, not a long lived thread. If you want to give the illusion that your Command is running a long time without tying up system resources the whole time, then write a RepeatedCommand. Below is an example RepeatedCommand that is used for the DefaultPoolManager in MPool:

                
/**
 * This is run every 10 seconds, starting after a 10 second delay.
 * It gives the appearance of being a long running thread, but it
 * does not consume a Thread for the times it would otherwise be
 * asleep.
 */
private static final class PoolManagerCommand
    implements RepeatedCommand
{
    private final BucketMap m_map;
    private final int m_min = 4;
    private final int m_max = 256;
    private final int m_grow = 4;

    protected PoolManagerCommand( BucketMap map )
    {
        m_map = map;
    }

    public long getDelayInterval()
    {
        return 10 * 1000L;
    }

    public long getRepeatInterval()
    {
        return 10 * 1000L;
    }

    /**
     * Anything less than one (zero or less) means to repeat as long
     * as the CommandManager is in service.
     */
    public int getNumberOfRepeats()
    {
        return -1;
    }

    public void execute()
        throws Exception
    {
        Iterator i = m_map.keySet().iterator();

        while( i.hasNext() )
        {
            ManagablePool pool = (ManagablePool)i.next();
            long key = ( (Long)m_map.get( pool ) ).longValue();
            int size = pool.size( key );

            if( size < m_min )
            {
                pool.grow( m_grow, key );
            }

            if( size > m_max )
            {
                pool.shrink( m_grow, key );
            }
        }
    }
}