/**
* Copyright (c) 2002-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.cluster.timeout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.neo4j.cluster.com.message.Message;
import org.neo4j.cluster.com.message.MessageProcessor;
import org.neo4j.cluster.com.message.MessageType;
/**
* Timeout management for state machines. First call setTimeout to setup a timeout.
* Then either the timeout will trigger or cancelTimeout will have been called with
* the key used to create the timeout.
*/
public class Timeouts
{
private long now = 0;
private MessageProcessor receiver;
private TimeoutStrategy timeoutStrategy;
private Map<Object, Timeout> timeouts = new HashMap<Object, Timeout>();
private List<Map.Entry<Object, Timeout>> triggeredTimeouts = new ArrayList<Map.Entry<Object, Timeout>>();
public Timeouts( MessageProcessor receiver, TimeoutStrategy timeoutStrategy )
{
this.receiver = receiver;
this.timeoutStrategy = timeoutStrategy;
}
/**
* Add a new timeout to the list
* If this is not cancelled it will trigger a message on the message processor
*
* @param key
* @param timeoutMessage
*/
public void setTimeout( Object key, Message<? extends MessageType> timeoutMessage )
{
long timeoutAt = now + timeoutStrategy.timeoutFor( timeoutMessage );
timeouts.put( key, new Timeout( timeoutAt, timeoutMessage ) );
}
/**
* Cancel a timeout corresponding to a particular key. Use the same key
* that was used to set it up.
*
* @param key
*/
public void cancelTimeout( Object key )
{
Timeout timeout = timeouts.remove( key );
if ( timeout != null )
{
timeoutStrategy.timeoutCancelled( timeout.timeoutMessage );
}
}
/**
* Cancel all current timeouts. This is typically used when shutting down.
*/
public void cancelAllTimeouts()
{
for ( Timeout timeout : timeouts.values() )
{
timeoutStrategy.timeoutCancelled( timeout.getTimeoutMessage() );
}
timeouts.clear();
}
public Map<Object, Timeout> getTimeouts()
{
return timeouts;
}
public Message<? extends MessageType> getTimeoutMessage( String timeoutName )
{
Timeout timeout = timeouts.get( timeoutName );
if ( timeout != null )
{
return timeout.getTimeoutMessage();
}
else
{
return null;
}
}
public void tick( long time )
{
synchronized ( this )
{
// Time has passed
now = time;
timeoutStrategy.tick( now );
// Check if any timeouts needs to be triggered
triggeredTimeouts.clear();
for ( Map.Entry<Object, Timeout> timeout : timeouts.entrySet() )
{
if ( timeout.getValue().checkTimeout( now ) )
{
triggeredTimeouts.add( timeout );
}
}
// Remove all timeouts that were triggered
for ( Map.Entry<Object, Timeout> triggeredTimeout : triggeredTimeouts )
{
timeouts.remove( triggeredTimeout.getKey() );
}
}
// Trigger timeouts
// This needs to be done outside of the synchronized block as it will trigger a message
// which will cause the statemachine to synchronize on Timeouts
for ( Map.Entry<Object, Timeout> triggeredTimeout : triggeredTimeouts )
{
triggeredTimeout.getValue().trigger( receiver );
}
}
public class Timeout
{
private long timeout;
private Message<? extends MessageType> timeoutMessage;
public Timeout( long timeout, Message<? extends MessageType> timeoutMessage )
{
this.timeout = timeout;
this.timeoutMessage = timeoutMessage;
}
public Message<? extends MessageType> getTimeoutMessage()
{
return timeoutMessage;
}
public boolean checkTimeout( long now )
{
if ( now >= timeout )
{
timeoutStrategy.timeoutTriggered( timeoutMessage );
return true;
}
else
{
return false;
}
}
public void trigger( MessageProcessor receiver )
{
receiver.process( timeoutMessage );
}
@Override
public String toString()
{
return timeout + ": " + timeoutMessage;
}
}
}