The management state (``MIB'') is a memory-only status readable
only by the managers, and writable only by the current leader. IManagementState,
provided by the Manager, gives access to it. See Section ![[*]](crossref.png) for more on properties, oids, and the management MIB:
for more on properties, oids, and the management MIB:
public interface IManagementState
{
    /**
     *  Set management state properties.  Only allowed on the
     *  leader.  All elements of PropertySet are set as a
     *  single transaction, and will be propagated to backups
     *  as a transaction.
     * @throws NotLeaderException   If called by a netlet
     *                              that's not the leader.
     */
    public void setProperties(PropertySet properties)
        throws NotLeaderException;
    /**
     *  Set a single management state property.  Only allowed on the
     *  leader.  
     * @throws NotLeaderException   If called by a netlet
     *                              that's not the leader.
     */
    public void setProperty(Oid key, Property value)
        throws NotLeaderException;
    /**
     *  Get a property set from the management state.  Transactional,
     *  i.e. properties will be fetched from a consistent state.
     *  Missing properties will be null.
     * @param keys  Array of oids whose properties we want to fetch.
     */
    public PropertySet getProperties(Oid[] keys);
    /**
     *  Get a single property from the management state.
     */
    public Property getProperty(Oid key);
    /** 
     * Apply an atomic change to the management MIB.
     * @param  changer  Something whose <code>apply</code> method will
     *                  apply a single change to the MIB.
     * @return    The object returned by the <code>changer</code>.
     * @throws NotLeaderException   If called by a netlet
     *                              that's not the leader.
     */
    public Object applyChange(IStateChanger changer) throws NotLeaderException;
    /** Get a read-only view of management state. Can be done by any manager.
     */
    public IPropertyView getView();
    /** Utility class helping lookup */
    public static class Get
    {
        /** Get the only instance of this plugin in the given manager.
         * Only the first such plugin is returned. If there isn't one then
         * an ArrayIndexOutOfBoundsException is thrown.
         */
        public static IManagementState one(IPluginManager pm)
        {
            return ((IManagementState)pm.lookup(IManagementState.class)[0]);
        } 
    }
}
Notice use of NotLeaderException, which is a checked exception and therefore needs to be caught. Even if the leader calls one of these methods this exception might be thrown, because leadership could change half-way through the call. The netlet should deal with it appropriately.
One clever way to deal with this is to catch the exception, then rethrow NoLongerLeaderException, which is in org.jtrix.project.beatrix.common. This is an unchecked (runtime) exception. By throwing this the unchecked exception will percolate all the way back to the access point, which can pick it up, attempt to rebind to a server (getting the new leader in the process) and trying the original method call again. The default access point does all this.
Also of relevance in IManagementState is the applyChange() method. It takes an IStateChanger interface as an argument:
public interface IStateChanger
{
    /** Apply a state change. Any changes applied here will not take effect
     * until the method returns.
     *
     * @param tree    Current management MIB, which this method will alter.
     * @return    Anything. This goes straight out and becomes the return
     *     value of the applyChange method of the IManagementState plugin.
     */
    public Object apply(ITypedDataTree tree);
}
The principle here is that an IStateChanger makes a single atomic change. Its apply() method takes the current MIB as an argument and alters it, perhaps based on its current content. Also, the Manager ensures such calls are synchronised, so two simultaneous calls will be sequenced. This means that read-only access to the management MIB is best done via the IManagementState.getView() method which avoids unnecessary bottlenecks.
The following example is taken from Jtrix.org's Webtrix servlet engine, and shows how the management state is used to find and create contracts. This is, of course, only a tiny fraction of the whole, but it gives us a taste of properties, IStateChanger, and more. Observe that by using an IStateChanger Webtrix obtains a ``lock'' on the MIB, so its next contract ID is unique:
public class ContractManager extends AbstractPlugin
	    implements IPlugin, IContractManager, IInternalFacetProvider, IContractInfoRequester
{
    IManagementState _state;
	// ...Vast amounts of work omitted   
    private int getNextContractID(ITypedDataTree tree)
    {
        Oid[] oids = tree.getNames(new Oid(WebtrixOids.CONTRACT, -1, WebtrixOids.NAME));
        int max = -1;
        for (int i=0; i<oids.length; i++)
        {
            int t = oids[i].getName()[1];
            if (max < t)
                max = t;
        }
        
        if (max == -1)
            return 0;
        else
            return max+1;
    }
    public synchronized Contract createContract(final X500DN key)
        throws ContractException
    {        
        if (findContract(key) != null)
            throw new ContractException("Contract already exists");
        Contract c;
        try
        {
            c = (Contract)_state.applyChange(new IStateChanger()
                {
                    public Object apply(ITypedDataTree tree)
                    {
                        int id = getNextContractID(tree);
                        Contract c = new Contract(key, id);
                        tree.setLeaf(new Oid(WebtrixOids.CONTRACT,
                                             id, WebtrixOids.NAME),
                                     new Property(key));
                        tree.setLeaf(new Oid(WebtrixOids.CONTRACT,
                                             id, WebtrixOids.CN_OBJECT),
                                     new Property(c));
                        return c;
                    }
                });
        }
        catch (NotLeaderException e)
        {
            // Percolate an unchecked exception back to the access point
            throw new NoLongerLeaderException();
        }
        c.setPluginManager(pluginManager());
        return c;
    }
    
    public Contract findContract(X500DN key)
    {
        ITypedStaticDataView view = _state.getView();
        PropertySet contracts = view.getTypedViewEntries(
            new Oid(WebtrixOids.CONTRACT, -1, WebtrixOids.NAME));
        Oid[] keys = contracts.getKeys();
        for (int i=0; i<keys.length; i++)
        {
            Property p = contracts.getProperty(keys[i]);
            if (p.getType() == Property.X500DN_TYPE && p.toX500DN().equals(key))
            {
                Oid o = new Oid(keys[i].getPrefix(2), WebtrixOids.CN_OBJECT);
                PropertySet contract_view = view.getTypedViewEntries(o);
                p = contract_view.getProperty(o);
                Contract c = (Contract)p.toObject();
                c.setPluginManager(pluginManager());
                return c;
            }
        }
        return null; 
    }
}
Nik Silver 2002-03-09