/**
* 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.protocol.omega;
import java.net.URI;
import org.neo4j.cluster.protocol.omega.payload.CollectResponsePayload;
import org.neo4j.cluster.protocol.omega.payload.RefreshAckPayload;
import org.neo4j.cluster.protocol.omega.payload.RefreshPayload;
import org.neo4j.cluster.com.message.Message;
import org.neo4j.cluster.com.message.MessageProcessor;
import org.neo4j.cluster.protocol.omega.payload.CollectPayload;
import org.neo4j.cluster.statemachine.State;
public enum OmegaState implements State<OmegaContext, OmegaMessage>
{
start
{
@Override
public OmegaState handle( OmegaContext context, Message<OmegaMessage> message,
MessageProcessor outgoing ) throws Throwable
{
switch ( message.getMessageType() )
{
case add_listener:
context.addListener( (OmegaListener) message.getPayload() );
break;
case remove_listener:
context.removeListener( (OmegaListener) message.getPayload() );
break;
case start:
context.startTimers();
return omega;
}
return this;
}
},
omega
{
@Override
public State<?, ?> handle( OmegaContext context, Message<OmegaMessage> message,
MessageProcessor outgoing ) throws Throwable
{
switch ( message.getMessageType() )
{
case refresh_timeout:
{
int refreshRoundId = context.startRefreshRound();
for ( URI server : context.getServers() )
{
outgoing.process( Message.to( OmegaMessage.refresh, server,
RefreshPayload.fromState( context.getMyState(),
refreshRoundId ) ) );
}
}
break;
case refresh:
{
org.neo4j.cluster.protocol.omega.state.State state = RefreshPayload.toState(
(RefreshPayload) message.getPayload() ).other();
URI from = new URI( message.getHeader( Message.FROM ) );
org.neo4j.cluster.protocol.omega.state.State currentForThisHost = context.getRegistry().get( from );
if ( currentForThisHost == null || currentForThisHost.compareTo( state ) < 0 )
{
context.getRegistry().put( from, state );
}
outgoing.process( Message.to(
OmegaMessage.refresh_ack,
from,
RefreshAckPayload.forRefresh( (RefreshPayload) message
.getPayload() ) ) );
}
break;
case refresh_ack:
{
RefreshAckPayload ack = message.getPayload();
// TODO deal with duplicate/corrupted messages here perhaps?
int refreshRound = ack.round;
context.ackReceived( refreshRound );
if ( context.getAckCount( refreshRound ) > context.getClusterNodeCount() / 2 )
{
context.getMyState().increaseFreshness();
context.roundDone( refreshRound );
}
}
break;
case round_trip_timeout:
{
context.getMyView().setExpired( true );
context.getMyState().getEpochNum().increaseSerialNum();
}
break;
case read_timeout:
{
int collectRound = context.startCollectionRound();
for ( URI server : context.getServers() )
{
outgoing.process( Message.to(
OmegaMessage.collect,
server,
new CollectPayload( collectRound ) ) );
}
}
break;
case collect:
{
CollectPayload payload = message.getPayload();
URI from = new URI( message.getHeader( Message.FROM ) );
int readNum = payload.getReadNum();
outgoing.process( Message.to( OmegaMessage.status,
from,
CollectResponsePayload.fromRegistry( context.getRegistry(), readNum ) ) );
}
break;
case status:
{
CollectResponsePayload payload = message.getPayload();
URI from = new URI( message.getHeader( Message.FROM ) );
int readNum = payload.getReadNum();
context.responseReceivedForRound( readNum, from, CollectResponsePayload.fromPayload( payload ));
if ( context.getStatusResponsesForRound( readNum ) > context.getClusterNodeCount() / 2 )
{
context.collectionRoundDone( readNum );
}
}
break;
default:
throw new IllegalArgumentException( message.getMessageType() +" is unknown" );
}
return this;
}
};
}