package eu.hgross.blaubot.core.statemachine.states; import java.util.List; import eu.hgross.blaubot.core.IBlaubotConnection; import eu.hgross.blaubot.core.IBlaubotDevice; import eu.hgross.blaubot.core.acceptor.ConnectionMetaDataDTO; import eu.hgross.blaubot.core.statemachine.BlaubotAdapterHelper; import eu.hgross.blaubot.core.statemachine.StateMachineSession; import eu.hgross.blaubot.core.statemachine.events.AbstractBlaubotDeviceDiscoveryEvent; import eu.hgross.blaubot.core.statemachine.events.AbstractTimeoutStateMachineEvent; import eu.hgross.blaubot.core.statemachine.events.DiscoveredKingEvent; import eu.hgross.blaubot.core.statemachine.states.PeasantState.ConnectionAccomplishmentType; import eu.hgross.blaubot.admin.ACKPronouncePrinceAdminMessage; import eu.hgross.blaubot.admin.AbstractAdminMessage; import eu.hgross.blaubot.admin.BowDownToNewKingAdminMessage; import eu.hgross.blaubot.admin.PrinceFoundAKingAdminMessage; import eu.hgross.blaubot.admin.PronouncePrinceAdminMessage; import eu.hgross.blaubot.util.Log; /** * * @author Henning Gross {@literal (mail.to@henning-gross.de)} * */ public class PrinceState implements IBlaubotState, IBlaubotSubordinatedState { private static final int MAX_RETRIES_ON_BOW_DOWN_CONNECTION = 4; private static final String LOG_TAG = "PrinceState"; private StateMachineSession session; private IBlaubotConnection kingConnection; /** * @param kingConnection * the connection to our king */ public PrinceState(IBlaubotConnection kingConnection) { this.kingConnection = kingConnection; if (kingConnection == null) throw new NullPointerException(); } @Override public IBlaubotState onConnectionEstablished(IBlaubotConnection connection) { return this; } @Override public IBlaubotState onConnectionClosed(IBlaubotConnection connection) { if (connection == kingConnection) { // -- we are prince and lost our king connection if (Log.logDebugMessages()) { Log.d(LOG_TAG, "We lost a connection in PrinceState. We assume our king to be lost. We are the prince so we change into the KingState"); } return new KingState(); } return this; } @Override public IBlaubotState onDeviceDiscoveryEvent(AbstractBlaubotDeviceDiscoveryEvent discoveryEvent) { if (discoveryEvent instanceof DiscoveredKingEvent) { IBlaubotDevice remoteDevice = discoveryEvent.getRemoteDevice(); if (Log.logDebugMessages()) { Log.d(LOG_TAG, "I discovered another king and will inform our king"); } int connectedDevices = session.getConnectionManager().getAllConnections().size(); // assert there is only the king connection if (connectedDevices != 1) { String errorMsg = "Inconsistant network state! There are " + connectedDevices + " connected devices (should be exactly 1 (= king))."; if (Log.logErrorMessages()) { Log.e(LOG_TAG, errorMsg); } // remain in prince state return this; } final IBlaubotDevice kingDevice = session.getConnectionManager().getAllConnections().get(0).getRemoteDevice(); final List<ConnectionMetaDataDTO> metaDataList = discoveryEvent.getConnectionMetaData(); final PrinceFoundAKingAdminMessage princeFoundAKingAdminMessage = new PrinceFoundAKingAdminMessage(remoteDevice.getUniqueDeviceID(), metaDataList); // send messsage to (and only to) king device session.getChannelManager().publishToSingleDevice(princeFoundAKingAdminMessage.toBlaubotMessage(), kingDevice.getUniqueDeviceID()); } return this; } @Override public void handleState(StateMachineSession session) { this.session = session; //session.getServerConnectionManager().setMaster(false); // is set by peasant state already BlaubotAdapterHelper.stopAcceptors(session.getConnectionStateMachine().getConnectionAcceptors()); BlaubotAdapterHelper.setDiscoveryActivated(session.getBeaconService(), true); } @Override public IBlaubotState onAdminMessage(AbstractAdminMessage adminMessage) { if (adminMessage instanceof PronouncePrinceAdminMessage) { PronouncePrinceAdminMessage ppam = (PronouncePrinceAdminMessage) adminMessage; if (Log.logDebugMessages()) { Log.d(LOG_TAG, "We got a PronouncePrinceAdminMessage. The new prince is " + ppam.getUniqueDeviceId()); } String newPrinceUniqueId = ppam.getUniqueDeviceId(); // check if we are NOT pronounced as prince and need to back off boolean weArePrince = session.isOwnDevice(newPrinceUniqueId); if (weArePrince) { if (Log.logDebugMessages()) { Log.d(LOG_TAG, "We are still prince, sending ACK and remaining in prince state."); } final String ourUniqueId = newPrinceUniqueId; final List<ConnectionMetaDataDTO> ownConnectionMetaDataList = session.getBeaconService().getCurrentBeaconMessage().getOwnConnectionMetaDataList(); final ACKPronouncePrinceAdminMessage ackMsg = new ACKPronouncePrinceAdminMessage(ourUniqueId, ownConnectionMetaDataList); session.getChannelManager().broadcastAdminMessage(ackMsg.toBlaubotMessage()); } else { if (Log.logDebugMessages()) { Log.d(LOG_TAG, "We are not prince anymore - changing to peasant state. "); } return new PeasantState(kingConnection, ConnectionAccomplishmentType.DEGRADATION); } } else if (adminMessage instanceof BowDownToNewKingAdminMessage) { BowDownToNewKingAdminMessage bowDownToNewKingAdminMessage = (BowDownToNewKingAdminMessage) adminMessage; if (Log.logDebugMessages()) { Log.d(LOG_TAG, "We got a BowDownToNewKingAdminMessage. The new king is " + bowDownToNewKingAdminMessage.getNewKingsUniqueDeviceId()); Log.d(LOG_TAG, "Trying to connect to new king ..."); } IBlaubotConnection conn = session.getConnectionManager().connectToBlaubotDevice(bowDownToNewKingAdminMessage.getNewKingsUniqueDeviceId(), MAX_RETRIES_ON_BOW_DOWN_CONNECTION); boolean result = conn != null; if (result) { if (Log.logDebugMessages()) { Log.d(LOG_TAG, "Connection to new king successful. Changing to peasant state."); } return new PeasantState(conn, ConnectionAccomplishmentType.BOWED_DOWN); } else { if (Log.logDebugMessages()) { Log.w(LOG_TAG, "Connection to new king failed! Oh my, now we are an outlaw :-(. Changing to FreeState to find a new king."); } return new FreeState(); } } return this; } @Override public String toString() { return "PrinceState"; } @Override public String getKingUniqueId() { return kingConnection.getRemoteDevice().getUniqueDeviceID(); } @Override public IBlaubotState onTimeoutEvent(AbstractTimeoutStateMachineEvent timeoutEvent) { return this; } @Override public IBlaubotConnection getKingConnection() { return kingConnection; } }