public void changeMode()

in brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/HighAvailabilityManagerImpl.java [265:457]


    public void changeMode(HighAvailabilityMode startMode, boolean preventElectionOnExplicitStandbyMode, boolean failOnExplicitModesIfUnusual) {
        if (!running) {
            // if was not running then start as disabled mode, then proceed as normal
            LOG.info("HA changing mode to "+startMode+" from "+getInternalNodeState()+" when not running, forcing an intermediate start as DISABLED then will convert to "+startMode);
            start(HighAvailabilityMode.DISABLED);
        }
        if (getNodeState()==ManagementNodeState.FAILED || getNodeState()==ManagementNodeState.INITIALIZING) {
            if (startMode!=HighAvailabilityMode.DISABLED) {
                // if coming from FAILED (or INITIALIZING because we skipped start call) then treat as initializing
                setInternalNodeState(ManagementNodeState.INITIALIZING);
            }
        }
        
        ownNodeId = managementContext.getManagementNodeId();
        // TODO Small race in that we first check, and then we'll do checkMaster() on first poll,
        // so another node could have already become master or terminated in that window.
        ManagementNodeSyncRecord existingMaster = hasHealthyMaster();
        boolean weAreRecognisedAsMaster = existingMaster!=null && ownNodeId.equals(existingMaster.getNodeId());
        boolean weAreMasterLocally = getInternalNodeState()==ManagementNodeState.MASTER;
        
        // catch error in some tests where mgmt context has a different HA manager
        if (managementContext.getHighAvailabilityManager()!=this)
            throw new IllegalStateException("Cannot start an HA manager on a management context with a different HA manager!");
        
        if (weAreMasterLocally) {
            // demotion may be required; do this before triggering an election
            switch (startMode) {
            case MASTER:
            case AUTO:
            case DISABLED:
                // no action needed, will do anything necessary below (or above)
                break;
            case HOT_STANDBY: 
            case HOT_BACKUP: 
            case STANDBY: 
                demoteTo(ManagementNodeState.of(startMode).get()); break;
            default:
                throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
            }
        }
        
        ManagementNodeState oldState = getInternalNodeState();
        
        // now do election
        switch (startMode) {
        case AUTO:
            // don't care; let's start and see if we promote ourselves
            if (getInternalNodeState()==ManagementNodeState.INITIALIZING) {
                setInternalNodeState(ManagementNodeState.STANDBY);
            }
            publishAndCheck(true);
            switch (getInternalNodeState()) {
            case HOT_BACKUP:
                if (!nodeStateTransitionComplete) throw new IllegalStateException("Cannot switch to AUTO when in the middle of a transition to "+getInternalNodeState());
                // else change us to standby, desiring to go to hot standby, and continue to below
                setInternalNodeState(ManagementNodeState.STANDBY);
                startMode = HighAvailabilityMode.HOT_BACKUP;
            case HOT_STANDBY:
            case STANDBY:
                if (getInternalNodeState()==ManagementNodeState.STANDBY && oldState==ManagementNodeState.INITIALIZING && startMode!=HighAvailabilityMode.HOT_BACKUP
                        && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY)) {
                    // auto requested; not promoted; so it should become hot standby
                    startMode = HighAvailabilityMode.HOT_STANDBY;
                }
                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
                String masterNodeId = newState.getMasterNodeId();
                ManagementNodeSyncRecord masterNodeDetails = newState.getManagementNodes().get(masterNodeId);
                LOG.info("Management node "+ownNodeId+" running as HA " + getInternalNodeState() + " autodetected"
                        + (startMode == HighAvailabilityMode.HOT_STANDBY || startMode == HighAvailabilityMode.HOT_BACKUP ? 
                            " (will change to "+startMode+")" : "")
                        + ", " +
                    (Strings.isBlank(masterNodeId) ? "no master currently (other node should promote itself soon)" : "master "
                        + (existingMaster==null ? "(new) " : "")
                        + "is "+masterNodeId +
                        (masterNodeDetails==null || masterNodeDetails.getUri()==null ? " (no url)" : " at "+masterNodeDetails.getUri())));
                break;
            case MASTER:
                LOG.info("Management node "+ownNodeId+" running as HA MASTER autodetected");
                break;
            default:
                throw new IllegalStateException("Management node "+ownNodeId+" set to HA AUTO, encountered unexpected mode "+getInternalNodeState());
            }
            break;
        case MASTER:
            if (!failOnExplicitModesIfUnusual || existingMaster==null) {
                promoteToMaster();
                if (existingMaster!=null) {
                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly");
                } else {
                    LOG.info("Management node "+ownNodeId+" running as HA MASTER explicitly, stealing from "+existingMaster);
                }
            } else if (!weAreRecognisedAsMaster) {
                throw new IllegalStateException("Master already exists; cannot run as master (master "+existingMaster.toVerboseString()+"); "
                    + "to trigger a promotion, set a priority and demote the current master");
            } else {
                LOG.info("Management node "+ownNodeId+" already running as HA MASTER, when set explicitly");
            }
            break;
        case HOT_BACKUP:
            setInternalNodeState(ManagementNodeState.HOT_BACKUP);
            // then continue into next block
        case STANDBY:
        case HOT_STANDBY:
            if (startMode!=HighAvailabilityMode.HOT_BACKUP) {
                if (ManagementNodeState.isHotProxy(getInternalNodeState()) && startMode==HighAvailabilityMode.HOT_STANDBY) {
                    // if was hot_backup, we can immediately go hot_standby
                    setInternalNodeState(ManagementNodeState.HOT_STANDBY);
                } else {
                    // from any other state, set standby, then perhaps switch to hot_standby later on (or might become master in the next block)
                    setInternalNodeState(ManagementNodeState.STANDBY);
                }
            }
            if (ManagementNodeState.isStandby(getInternalNodeState())) {
                if (!preventElectionOnExplicitStandbyMode) {
                    publishAndCheck(true);
                }
                if (failOnExplicitModesIfUnusual && existingMaster==null) {
                    LOG.error("Management node "+ownNodeId+" detected no master when "+startMode+" requested and existing master required; failing.");
                    throw new IllegalStateException("No existing master; cannot start as "+startMode);
                }
            }
            String message = "Management node "+ownNodeId+" running as HA "+getNodeState()+" (";
            if (getNodeState().toString().equals(startMode.toString()))
                message += "explicitly requested";
            else if (startMode==HighAvailabilityMode.HOT_STANDBY && getNodeState()==ManagementNodeState.STANDBY)
                message += "caller requested "+startMode+", will attempt rebind for HOT_STANDBY next";
            else
                message += "caller requested "+startMode;
            
            if (getNodeState()==ManagementNodeState.MASTER) {
                message += " but election re-promoted this node)";
            } else {
                ManagementPlaneSyncRecord newState = loadManagementPlaneSyncRecord(true);
                if (Strings.isBlank(newState.getMasterNodeId())) {
                    message += "); no master currently"; 
                    if (startMode != HighAvailabilityMode.HOT_BACKUP) message += " (subsequent election may repair)";
                } else {
                    message += "); master "+newState.getMasterNodeId();
                }
            }
            LOG.info(message);
            break;
        case DISABLED:
            // safe just to run even if we weren't master
            LOG.info("Management node "+ownNodeId+" HA DISABLED (was "+getInternalNodeState()+")");
            demoteTo(ManagementNodeState.FAILED);
            if (pollingTask!=null) pollingTask.cancel(true);
            break;
        default:
            throw new IllegalStateException("Unexpected high availability mode "+startMode+" requested for "+this);
        }
        
        if ((startMode==HighAvailabilityMode.HOT_STANDBY || startMode==HighAvailabilityMode.HOT_BACKUP)) {
            if (!ManagementNodeState.isHotProxy(oldState)) {
                // now transition to hot proxy
                nodeStateTransitionComplete = false;
                if (startMode==HighAvailabilityMode.HOT_STANDBY) {
                    // if it should be hot standby, then we may need to promote
                    // inform the world that we are transitioning (but not eligible for promotion while going in to hot standby)
                    // (no harm in doing this twice)
                    publishHealth();
                }
                try {
                    activateHotProxy(ManagementNodeState.of(startMode).get()).get();
                    // error above now throws
                    nodeStateTransitionComplete = true;
                    publishHealth();

                    if (getNodeState()==ManagementNodeState.HOT_STANDBY || getNodeState()==ManagementNodeState.HOT_BACKUP) {
                        LOG.info("Management node "+ownNodeId+" now running as HA "+getNodeState()+"; "
                            + managementContext.getApplications().size()+" application"+Strings.s(managementContext.getApplications().size())+" loaded");
                    } else {
                        // shouldn't come here, we should have gotten an error above
                        LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); "
                            + "(see log for further details)");
                    }
                } catch (Exception e) {
                    LOG.warn("Management node "+ownNodeId+" unable to promote to "+startMode+" (currently "+getNodeState()+"); rethrowing: "+Exceptions.collapseText(e));
                    nodeStateTransitionComplete = true;
                    throw Exceptions.propagate(e);
                }
            } else {
                // transitioning among hot proxy states - tell the rebind manager
                managementContext.getRebindManager().stopReadOnly();
                managementContext.getRebindManager().startReadOnly(ManagementNodeState.of(startMode).get());
                nodeStateTransitionComplete = true;
            }
        } else {
            nodeStateTransitionComplete = true;
        }
        if (startMode!=HighAvailabilityMode.DISABLED)
            registerPollTask();
    }