/** * Starts the Listener so it's actively listening for election results and broadcasts of * replication group changes. * * <p>The Monitor must have been previously registered with the replication group via the <code> * register()</code> method so that other nodes in the group are aware of it and can keep it * current. If the monitor has not been registered, it will not be updated, that is, its listener * will not be invoked. The registration needs to be done exactly once. * * <p>Once the registration has been completed, the Monitor can start listening even when none of * the other nodes in the group are available. It will be contacted automatically by the other * nodes when they come up and hold a successful election. * * <p>Invoking <code>startListener</code> results in a synchronous callback to the application via * the {@link MonitorChangeListener#notify(NewMasterEvent)} method, if there is a current Master. * If there is no Master at this time then the callback takes place asynchronously, after the * method returns, when a Master is eventually elected. * * @param newListener the listener used to monitor events of interest. * @throws EnvironmentFailureException if an unexpected, internal or environment-wide failure * occurs. * @throws IOException if the monitor socket could not be set up * @throws IllegalArgumentException if an invalid parameter is specified. * @throws IllegalStateException if the monitor has been shutdown, or a listener has already been * established. */ public void startListener(MonitorChangeListener newListener) throws DatabaseException, IOException { if (shutdown.get()) { throw new IllegalStateException("The monitor has been shutdown"); } if (newListener == null) { throw new IllegalArgumentException( "A MonitorChangeListener must be associated with " + " this Monitor when invoking this method"); } if (this.monitorChangeListener != null) { throw new IllegalStateException("A Listener has already been established"); } this.monitorChangeListener = newListener; serviceDispatcher = new ServiceDispatcher(monitorConfig.getNodeSocketAddress()); serviceDispatcher.start(); Protocol electionProtocol = new Protocol( TimebasedProposalGenerator.getParser(), MasterValue.getParser(), monitorConfig.getGroupName(), nameIdPair, null); learner = new Learner(electionProtocol, serviceDispatcher, nameIdPair); serviceDispatcher.register(new MonitorService(this, serviceDispatcher)); masterChangeListener = new MasterChangeListener(); learner.addListener(masterChangeListener); learner.start(); try { /* Notify the listener about the current master. */ final ReplicationGroup repGroup = repGroupAdmin.getGroup(); final RepGroupImpl group = RepInternal.getRepGroupImpl(repGroup); /* * In the absence of a network failure, the query should result in * a call to the notify method of MonitorChanegListener. */ learner.queryForMaster(group.getLearnerSockets()); /* Notify JoinGroupEvents for those current active nodes. */ notifyJoinGroupEventsForActiveNodes(repGroup); } catch (UnknownMasterException ume) { /* The Listener will be informed when a Master is elected. */ LoggerUtils.logMsg(logger, formatter, Level.INFO, "No current master."); } }
/** Implements the Listener protocol. */ @SuppressWarnings("unused") public void notify(Proposal proposal, Value value) { /* We have a winning new proposal, is it truly different? */ if (value.equals(currentValue)) { return; } currentValue = (MasterValue) value; try { String currentMasterName = currentValue.getNodeName(); LoggerUtils.logMsg( logger, formatter, Level.INFO, "Monitor notified of new Master: " + currentMasterName); if (monitorChangeListener == null) { /* No interest */ return; } monitorChangeListener.notify(new NewMasterEvent(currentValue)); } catch (Exception e) { LoggerUtils.logMsg( logger, formatter, Level.SEVERE, "Monitor change event processing exception: " + e.getMessage()); } }