head	1.19;
access;
symbols
	TIGRIS_1_1_0RC2:1.8.2.6
	TIGRIS_1_1_0RC1:1.8.2.6
	TIGRIS_1_1:1.8.2.6.0.2
	TIGRIS_1_0_8:1.8.2.6
	TIGRIS_1_0_8RC3:1.8.2.6
	TIGRIS_1_0_8RC2:1.8.2.6
	TIGRIS_1_0_8RC1:1.8.2.6
	TIGRIS_1_0_7:1.8.2.6
	TIGRIS_1_0_7RC3:1.8.2.6
	TIGRIS_1_0_7RC2:1.8.2.6
	TIGRIS_1_0_7RC1:1.8.2.6
	TIGRIS_1_0_6:1.8.2.6
	TIGRIS_1_0_6RC5:1.8.2.6
	TIGRIS_1_0_6RC4:1.8.2.6
	TIGRIS_1_0_6RC3:1.8.2.6
	TIGRIS_1_0_6RC2:1.8.2.5
	TIGRIS_1_0_6RC1:1.8.2.2
	TIGRIS_1_0:1.8.0.2;
locks; strict;
comment	@# @;


1.19
date	2001.08.17.19.02.19;	author leonardr;	state Exp;
branches;
next	1.18;

1.18
date	2001.08.11.09.08.09;	author dlr;	state Exp;
branches;
next	1.17;

1.17
date	2001.08.03.17.43.55;	author dlr;	state Exp;
branches;
next	1.16;

1.16
date	2001.08.03.17.07.37;	author dlr;	state Exp;
branches;
next	1.15;

1.15
date	2001.08.03.02.41.14;	author dlr;	state Exp;
branches;
next	1.14;

1.14
date	2001.08.01.04.39.26;	author dlr;	state Exp;
branches;
next	1.13;

1.13
date	2001.07.26.21.53.08;	author dlr;	state Exp;
branches;
next	1.12;

1.12
date	2001.06.07.22.44.40;	author leonardr;	state Exp;
branches;
next	1.11;

1.11
date	2001.06.07.16.44.55;	author leonardr;	state Exp;
branches;
next	1.10;

1.10
date	2001.06.07.02.24.13;	author leonardr;	state Exp;
branches;
next	1.9;

1.9
date	2001.06.07.01.57.23;	author leonardr;	state Exp;
branches;
next	1.8;

1.8
date	2001.06.01.15.55.32;	author leonardr;	state Exp;
branches
	1.8.2.1;
next	1.7;

1.7
date	2001.05.23.00.35.01;	author leonardr;	state Exp;
branches;
next	1.6;

1.6
date	2001.05.22.20.34.56;	author leonardr;	state Exp;
branches;
next	1.5;

1.5
date	2001.05.22.01.00.03;	author leonardr;	state Exp;
branches;
next	1.4;

1.4
date	2001.05.22.00.48.51;	author leonardr;	state Exp;
branches;
next	1.3;

1.3
date	2001.05.22.00.14.19;	author leonardr;	state Exp;
branches;
next	1.2;

1.2
date	2001.05.21.21.06.01;	author leonardr;	state Exp;
branches;
next	1.1;

1.1
date	2001.05.18.21.56.26;	author leonardr;	state Exp;
branches;
next	;

1.8.2.1
date	2001.06.01.19.18.08;	author leonardr;	state Exp;
branches;
next	1.8.2.2;

1.8.2.2
date	2001.06.07.02.02.18;	author leonardr;	state Exp;
branches;
next	1.8.2.3;

1.8.2.3
date	2001.06.07.16.50.28;	author leonardr;	state Exp;
branches;
next	1.8.2.4;

1.8.2.4
date	2001.06.07.17.11.17;	author stack;	state Exp;
branches;
next	1.8.2.5;

1.8.2.5
date	2001.06.07.18.28.49;	author leonardr;	state Exp;
branches;
next	1.8.2.6;

1.8.2.6
date	2001.06.07.21.24.15;	author leonardr;	state Exp;
branches;
next	;


desc
@@


1.19
log
@The doService method hasn't been used by ClusteredSingletonService in quite a while.
@
text
@/* ================================================================
 * Copyright (c) 2001 CollabNet.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 * any, must include the following acknowlegement: "This product includes
 * software developed by CollabNet <http://www.collab.net/>."
 * Alternately, this acknowlegement may appear in the software itself, if
 * and wherever such third-party acknowlegements normally appear.
 *
 * 4. The hosted project names must not be used to endorse or promote
 * products derived from this software without prior written
 * permission. For written permission, please contact info@@collab.net.
 *
 * 5. Products derived from this software may not use the "Tigris" or
 * "Helm" names nor may "Tigris" or "Helm" appear in their names without
 * prior written permission of CollabNet.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of CollabNet.
 */

package org.tigris.helm.tool.cluster;

import java.io.InputStream;

import org.apache.fulcrum.*;
import org.apache.turbine.util.Log;
import org.apache.commons.util.exception.NestableException;

import org.tigris.helm.event.ClusterEvent;
import org.tigris.helm.event.SingletonElectionEvent;
import org.tigris.helm.event.SingletonRecessEvent;
import org.tigris.helm.om.*;
import org.tigris.helm.tool.HelmService;
import org.tigris.helm.tool.TemplateTool;
import org.tigris.helm.tool.cluster.Clustered;

/**
 * A service of which there is only one instance across an entire
 * cluster.
 *
 * @@author <a href="mailto:leonardr@@collab.net">Leonard Richardson</a>
 * @@author <a href="mailto:dlr@@collab.net">Daniel Rall</a>
 */
public abstract class ClusteredSingletonService extends BaseService
    implements Runnable, HelmService, Clustered
{
    /**
     * The value indicating that the clustered could not determine its
     * bid.
     */
    public static final float NO_BID = -1;

    /**
     * Whether or not to log debugging information;
     */
    private static final boolean DEBUG = true;

    /**
     * A token to use to parse the system load.
     */
    private static final String LOAD_PARSE_TOKEN = "load average: ";

    /**
     * Whether this instance is the single instance for the cluster.
     */
    protected boolean isSingleton = false;

    /**
     * Whether to stop the event listener.
     */
    protected boolean stop = false;

    /**
     * The event listener thread for this service instance.
     */
    protected Thread meAsThread = null;

    /**
     * Indicates whether the event listener is running.
     */
    private boolean isRunning = false;

    /**
     * Gets the descriptor used to key information for this type of service
     * (currently used in the database).
     */
    public abstract String getDescriptor();

    /**
     * Returns the relative path to the template which contains controls
     * for controlling this service.
     */
    public String getAdminTemplateName() 
    {
        return TemplateTool.getPathToServiceAdminTemplate(getName() + ".vm");
    }

    public void setActive(boolean active)
    {
        isSingleton = active;
    }

    /**
     * Puts in a bid for cluster-wide singletonhood, determined via
     * <code>uptime</code>.
     *
     * <pre>
     * shell$ uptime
     *   4:35pm  up 23 days, 15:08, 32 users,  load average: 0.38, 0.52, 0.54
     * </pre>
     *
     * @@return The system load, or <code>-1L</code> if load could not
     * be determined.
     */
    public float bid()
        throws NestableException
    {
        float load = NO_BID;
        try
        {
            // Exec binary to get server load.
            Process p = Runtime.getRuntime().exec("/usr/bin/uptime");
            if (p.waitFor() == 0)
            {
                InputStream in = p.getInputStream();
                StringBuffer buf = new StringBuffer(in.available());
                int ch;
                while ((ch = in.read()) != -1)
                {
                    buf.append((char) ch);
                }
                in.close();

                // Parse load from the output of uptime.
                String toParse = buf.toString();
                int i = toParse.indexOf(LOAD_PARSE_TOKEN);
                if (i > 0)
                {
                    i += LOAD_PARSE_TOKEN.length();
                    int j = toParse.indexOf(',', i);
                    if (j > 0)
                    {
                        load = Float.parseFloat(buf.substring(i, j));
                    }
                }
                try
                {
                    in.close();
                }
                catch (Exception ignored)
                {
                }
            }
        }
        catch (Exception e)
        {
            throw new NestableException(e);
        }
        return load;
    }

    /**
     * Returns whether or not the singleton is currently claimed by some
     * JVM.
     */
    public boolean isRunning()
        throws Exception
    {
        return isRunning;
    }

    /**
     * Callback for reception of event by
     * {@@link org.tigris.helm.tool.cluster.ClusterService}.
     *
     * @@see org.tigris.helm.tool.cluster.Clustered
     */
    public void eventReceived(ClusterEvent event)
        throws NestableException
    {
        if (event instanceof SingletonRecessEvent)
        {
            if (isSingleton)
            {
                isSingleton = false;
            }
        }
        else if (event instanceof SingletonElectionEvent)
        {
            Log.warn(SERVICE_NAME + ": " + event.getClass().getName() +
                     " event received -- this type should be filtered out " +
                     "and handled by " +
                     ClusterService.getService().getClass().getName());
        }
        else
        {
            Log.warn(SERVICE_NAME + ": Unhandled event received: " + event);
        }
    }

    /**
     * Restarts all instances in the cluster.
     */
    public void startRunning()
        throws NestableException
    {
        sendEvent(new SingletonElectionEvent(bid()));
    }

    /**
     * Stops all instances in the cluster.
     */
    public void stopRunning()
        throws NestableException
    {
        sendEvent(new SingletonRecessEvent(getDescriptor()));
    }

    /**
     * Macro to send an event for this instance using the
     * {@@link org.tigris.helm.tool.cluster.ClusterService}.
     */
    protected final void sendEvent(ClusterEvent event)
        throws NestableException
    {
        ClusterService.getService().sendEvent(this, event);
    }

    /**
     * Initializes this service.  Called during Fulcrum
     * initialization.
     */
    public synchronized void init()
        throws InitializationException
    {
        
        meAsThread = new Thread(this);
        setInit(true);

        meAsThread.start();
    }

    /**
     * Shuts down this service, relenquishing control of singleton (if
     * we have it).
     */
    public final void shutdown()   
    {
        setInit(false);
        stop = true;
    }

    /**
     * Event listener loop.
     */
    public final void run()
    {
        isRunning = true;

        try
        {
            ClusterService.getService().register(this);
        }
        catch (NestableException e)
        {
            Log.error(SERVICE_NAME + ": Registering with " +
                      ClusterService.SERVICE_NAME, e);
        }

        stop = false;
        while (!stop)
        {
            try
            {
                ClusterService.getService().receiveEvent(this);
            }
            catch (Exception e)
            {
                Log.error("Exception thrown in " + getDescriptor() 
                          + " singleton loop " , e);
            }
        }

        //We've been stopped. Relenquish control of the singleton.
        try
        {
            isSingleton = false;
            ClusterService.getService().unregister(this);
        }
        catch (NestableException e)
        {
            Log.error(SERVICE_NAME + ": Unregistering with " +
                      ClusterService.SERVICE_NAME, e);
        }

        isRunning = false;
    }
}
@


1.18
log
@Upgrading to Turbine 3.0-dev, Fulcrum 1.0, and Torque 1.0.
@
text
@a188 6
     * Hook method which runs service-specific code.
     */
    public abstract void doService()
        throws Exception;

    /**
@


1.17
log
@Corrected build error.
@
text
@a49 1
import javax.servlet.ServletConfig;
d51 1
a51 1
import org.apache.turbine.services.*;
d53 1
a53 1
import org.apache.turbine.util.TurbineException;
d70 1
a70 1
public abstract class ClusteredSingletonService extends TurbineBaseService
d142 1
a142 1
        throws TurbineException
d183 1
a183 1
            throw new TurbineException(e);
d211 1
a211 1
        throws TurbineException
d237 1
a237 1
        throws TurbineException
d246 1
a246 1
        throws TurbineException
d256 1
a256 1
        throws TurbineException
d262 2
a263 3
     * Called during <code>Turbine.init()</code> (i.e. early initialization).
     *
     * @@param config The configuration we're operating under.
d265 1
a265 1
    public synchronized void init(ServletConfig config)
d268 1
d296 1
a296 1
        catch (TurbineException e)
d322 1
a322 1
        catch (TurbineException e)
@


1.16
log
@* Made local variable init use a constant instead for what was
previously a magic number.

* Closed process InputStream.

* Put in correct [non-]handling of SingletonElectionEvent -- this type
of event is currently filtered and handled by ClusterService.  This
part of the cluster-level event handling system seems a bit clunky to
me, and could use work.
@
text
@d223 1
a223 1
            Log.warn(SERVICE_NAME + ": " event.getClass().getName() +
@


1.15
log
@Now with delicious Spread!
@
text
@d145 1
a145 1
        float load = -1L;
d173 7
d214 1
a214 1
        /*if (event instanceof SingletonElectionEvent)
d216 1
a216 2
            // Don't do anything if source is us.
            if (((SingletonElectionEvent) event).getSource() != this)
d218 1
a218 2
                // Respond with our bid.
                sendEvent(new SingletonElectionEvent(this, bid()));
d221 1
a221 1
        else*/ if (event instanceof SingletonRecessEvent)
d223 4
a226 4
            if (isSingleton)
            {
                isSingleton = false;
            }
@


1.14
log
@Added NO_BID constant to fix build (some code that's not in sync with
the current CVS tree slipped in).
@
text
@d49 1
a52 1
import org.apache.turbine.util.db.pool.DBConnection;
d54 1
d56 3
d62 1
d65 2
a66 5
 * Every JVM across a cluster will run one of these services. The
 * services monitor the HELM_TIMESTAMP database table and wait for a
 * chance to become the singleton for the cluster. The singleton does
 * whatever it does, making sure to keep the timestamp updated so that
 * other machines on the cluster don't usurp its responsibilities.
d72 1
a72 1
    implements Runnable, HelmService
d83 1
a83 1
    public static final boolean DEBUG = false;
d85 4
a88 1
    public static final String SINGLETON_EVENT_TYPE = "singleton";
d90 3
a92 1
    protected ClusterEvent lastStamp = null;
d94 4
d99 4
d106 5
d125 1
a125 5
    /**
     * Returns the time, in milliseconds, separating the singleton's
     * assertions of singletonhood.  
     */
    public long getPollTime()
d127 1
a127 1
        return 1000 * 5;
d131 10
a140 2
     * Returns the time, in milliseconds, which must elapse before a
     * claim on a singleton is considered dead.
d142 2
a143 1
    public long getTimeout()
d145 35
a179 1
        return (getPollTime() * 4) + 1000;
d195 1
a195 2
        ClusterEvent latestStamp = getClusterEvent(null);
        return (latestStamp.getStamp() != null);
d199 32
a230 1
     * Starts the service running after it had been stopped running.
d233 1
a233 1
        throws Exception
d235 1
a235 1
        ClusterEventPeer.deleteInstance(SINGLETON_EVENT_TYPE, getDescriptor());
d239 1
a239 1
     * Stops the service by disabling its singleton timestamp.
d242 11
a252 1
        throws Exception
d254 1
a254 3
        ClusterEvent latestStamp = getClusterEvent(null);
        latestStamp.setStamp(null);
        latestStamp.save();
d262 1
a262 1
    public void init(ServletConfig config)
d265 1
d267 1
a267 1
        meAsThread = new Thread(this);
d278 10
a287 1
        sendStopSignal();
d290 1
a290 1
            releaseSingleton();
d292 1
a292 1
        catch (Exception e)
d294 2
a295 3
            Log.error(e);
            throw new Error("Error releasing " + getDescriptor() 
                            + " singleton: " + e.getMessage());
a296 9
    }

    /**
     * Tells the thread that it should stop running.
     */
    protected void sendStopSignal()
    {
        stop = true;
    }
a297 7
    /**
     * Every iteration, attempts to become or remain the singleton.
     * If the attempt succeeds, do whatever it is the singleton is
     * supposed to do.  
     */
    public void run()
    {
d303 1
a303 13
                attemptToBecomeSingleton();
                if (isSingleton)
                {
                    doService();
                }
                try                             
                {
                    Thread.sleep(getPollTime());
                } 
                catch (InterruptedException resumeRunning)
                {
                    // The JVM wants us to get back to work.
                }
d315 2
a316 38
            releaseSingleton();
        }
        catch (Exception e)
        {
            Log.error("Exception thrown relenquishing control of " +
                      getDescriptor() + " singleton: " , e);
        }
    }
    
    /**
     * Returns the cluster event indicating the last time the cluster
     * singleton reasserted its claim.
     */
    public ClusterEvent getClusterEvent(DBConnection conn)
        throws Exception
    {
        return ClusterEventPeer.retrieveInstance(SINGLETON_EVENT_TYPE,
                                                 getDescriptor(), conn);
    }

    /**
     * If the cluster singleton is up for grabs, attempt to grab
     * it. Otherwise, stew and be bitter.  
     */
    protected void attemptToBecomeSingleton()
        throws Exception
    {
        ClusterEvent latestStamp = getClusterEvent(null);

        if (isSingleton && latestStamp == null)
        {
            //We were the singleton, but the service has been stopped.
            if (DEBUG)
            {
                Log.debug
                    ("I was the singleton, but the service has been stopped.");
            }
            releaseSingleton();
d318 1
a318 2

        if (isSingleton && latestStamp.newerThan(lastStamp))
d320 2
a321 8
            //We were the singleton, but someone else updated the timestamp
            //after we did. Let them have it.
            if (DEBUG)
            {
                Log.debug("I was the singleton, but " + latestStamp 
                          + " is newer than " + lastStamp + ".");
            }
            releaseSingleton();
a322 41
        else
        {
            if (latestStamp.isUnset()
                || latestStamp.ageExceeds(getTimeout())
                || isSingleton)
            {
                //There is no stamp, or it's older than the timeout allows,
                //or we're already the singleton.
                String[] lockRead = {};
                String[] lockWrite = {ClusterEventPeer.TABLE_NAME};
                
                DBConnection c = ClusterEventPeer.lockTables(lockRead,
                                                             lockWrite);
                try
                {
                    ClusterEvent newStamp = getClusterEvent(c);
                    if (latestStamp.equals(newStamp))
                    {
                        //No one has changed the timestamp; we now
                        //have a locked table and are free to become
                        //the singleton.
                        latestStamp.freshen();
                        latestStamp.save(c);
                        lastStamp = getClusterEvent(c);
                        isSingleton = true;
                    }
                    else
                    {
                        //Someone else changed the timestamp between the time
                        //we got it and the time we locked the table. Whoever
                        //that was is now the singleton.
                        isSingleton = false;
                    }
                }
                finally
                {
                    ClusterEventPeer.unlockTables(c);
                }
            }
        }
    }
d324 1
a324 10
    protected void releaseSingleton()
        throws Exception
    {
        lastStamp = null;
        if (isSingleton)
        {
            isSingleton = false;
            ClusterEventPeer.deleteInstance(SINGLETON_EVENT_TYPE,
                                            getDescriptor());
        }
a325 1

@


1.13
log
@Removed whitespace.
@
text
@d73 6
@


1.12
log
@Changed spacing.
@
text
@d172 1
a172 1
    {        
d246 1
a246 1
    {        
@


1.11
log
@Always unlock the tables no matter what.
@
text
@d300 3
a302 2
                        //No one has changed the timestamp; we now have a locked
                        //table and are free to become the singleton.
@


1.10
log
@Controls for services now go right next to their statuses. Also added status for TaskRunnerService
@
text
@d295 1
a295 2
                ClusterEvent newStamp = getClusterEvent(c);
                if (latestStamp.equals(newStamp))
d297 17
a313 6
                    //No one has changed the timestamp; we now have a locked
                    //table and are free to become the singleton.
                    latestStamp.freshen();
                    latestStamp.save(c);
                    lastStamp = getClusterEvent(c);
                    isSingleton = true;
d315 1
a315 1
                else
d317 1
a317 4
                    //Someone else changed the timestamp between the time
                    //we got it and the time we locked the table. Whoever
                    //that was is now the singleton.
                    isSingleton = false;
a318 1
                ClusterEventPeer.unlockTables(c);
@


1.9
log
@Always unlock the table.
@
text
@d57 1
d89 9
@


1.8
log
@Implemented singleton service stopping.
@
text
@d56 1
d69 1
a69 1
    implements Runnable
a293 1
                    ClusterEventPeer.unlockTables(c);
d302 1
@


1.8.2.1
log
@Ported minimal cluster functionality so that I could also port IssueTrackingRebuildService, which builds on it.
@
text
@a48 1
import java.util.Date;
a51 1
import org.apache.turbine.util.db.Criteria;
d289 1
a289 12

                    //Stupid hack to get around bug in old Turbine (?)
                    Criteria cr = new Criteria(2)
                        .add(ClusterEventPeer.TYPE, SINGLETON_EVENT_TYPE)
                        .add(ClusterEventPeer.DESCRIPTOR, getDescriptor());
                    ClusterEventPeer.doDelete(cr,c);

                    latestStamp = new ClusterEvent();
                    latestStamp.setType(SINGLETON_EVENT_TYPE);
                    latestStamp.setDescriptor(getDescriptor());
                    latestStamp.setStamp(new Date());

a290 1

@


1.8.2.2
log
@Always unlock tables.
@
text
@d307 1
a315 1
                ClusterEventPeer.unlockTables(c);
@


1.8.2.3
log
@Always unlock the tables.
@
text
@d286 2
a287 1
                try
d289 25
a313 30
                    ClusterEvent newStamp = getClusterEvent(c);
                    if (latestStamp.equals(newStamp))
                    {
                        //No one has changed the timestamp; we now
                        //have a locked table and are free to become
                        //the singleton.
                        
                        //Stupid hack to get around bug in old Turbine (?)
                        Criteria cr = new Criteria(2)
                            .add(ClusterEventPeer.TYPE, SINGLETON_EVENT_TYPE)
                            .add(ClusterEventPeer.DESCRIPTOR, getDescriptor());
                        ClusterEventPeer.doDelete(cr,c);
                        
                        latestStamp = new ClusterEvent();
                        latestStamp.setType(SINGLETON_EVENT_TYPE);
                        latestStamp.setDescriptor(getDescriptor());
                        latestStamp.setStamp(new Date());
                        
                        latestStamp.save(c);
                        
                        lastStamp = getClusterEvent(c);
                        isSingleton = true;
                    }
                    else
                    {
                        //Someone else changed the timestamp between the time
                        //we got it and the time we locked the table. Whoever
                        //that was is now the singleton.
                        isSingleton = false;
                    }
a314 3
            }
            finally
            {
a315 1
            }
@


1.8.2.4
log
@Moved finally block location to sync w/ try block (Was inserted at wrong
side of a parenthesis).
Reviewed by: Leonard
@
text
@d319 5
a323 5

                finally
                {
                    ClusterEventPeer.unlockTables(c);
                }
@


1.8.2.5
log
@Factored out duplicate Turbine workaroudn code and got rid of table-locking deadlocks.
@
text
@d294 13
a306 2
                        latestStamp.freshen();
                        latestStamp.replace(c);
@


1.8.2.6
log
@Sidestep Turbine altogether for this thing that causes the quoting bug.
@
text
@d294 2
a295 1
                        latestStamp.saveNewStamp(c);
@


1.7
log
@Cleanup of caching and cache invalidation.
@
text
@d112 4
d117 1
d119 2
a120 1
        return true;
d123 3
a125 2
    // HELP: Unsure what to do with start/stopRunning() methods.

d127 1
d129 1
d132 3
d136 1
d138 3
d248 11
@


1.6
log
@Changed poll time and singleton timeout to be more configurable.
@
text
@d70 5
d237 5
a241 1
            Log.debug("I was the singleton, but " + latestStamp + " is newer than " + lastStamp + ".");
@


1.5
log
@Increased timeout.
@
text
@a71 4
    public static final long TIMESTAMP_TIMEOUT = 1000 * 60;

    public static final long POLL_TIME = 1000 * 5;

d84 18
d182 1
a182 1
                    Thread.sleep(POLL_TIME);
d238 1
a238 1
                || latestStamp.ageExceeds(TIMESTAMP_TIMEOUT)
@


1.4
log
@Release the singleton on shutdown.
@
text
@d72 1
a72 1
    public static final long TIMESTAMP_TIMEOUT = 1000 * 10;
@


1.3
log
@Bug fixes for clustering.
@
text
@d72 1
a72 1
    public static final long TIMESTAMP_TIMEOUT = 1000 * 60 * 5;
d129 10
@


1.2
log
@Moved clustering features out of RMI and into the database.
@
text
@a202 1
        Log.debug("ClusteredSingletonService attempting to become singleton");
@


1.1
log
@Fixed up cluster singleton interface and made the task runner into a cluster singleton.
@
text
@d72 1
a72 1
    public static final long TIMESTAMP_TIMEOUT = 1000 * 10;
a115 1
        Log.debug("Hello from ClusteredSingletonService late init");
@

