001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ha.singleton;
023:
024: import java.util.List;
025:
026: import javax.management.Notification;
027:
028: import org.jboss.ha.framework.interfaces.ClusterMergeStatus;
029: import org.jboss.ha.jmx.HAServiceMBeanSupport;
030:
031: /**
032: * Base class for HA-Singleton services.
033: *
034: * @author <a href="mailto:ivelin@apache.org">Ivelin Ivanov</a>
035: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
036: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
037: * @version $Revision: 61770 $
038: */
039: public class HASingletonSupport extends HAServiceMBeanSupport implements
040: HASingletonSupportMBean, HASingleton {
041: // Private Data --------------------------------------------------
042:
043: private boolean isMasterNode = false;
044: private HASingletonElectionPolicy mElectionPolicyMB = null;
045: private boolean restartOnMerge = true;
046:
047: // Constructors --------------------------------------------------
048:
049: /**
050: * Default CTOR
051: */
052: public HASingletonSupport() {
053: // empty
054: }
055:
056: // Attributes ----------------------------------------------------
057:
058: /**
059: * @jmx:managed-attribute
060: *
061: * @return true if this cluster node has the active mbean singleton, false otherwise
062: */
063: public boolean isMasterNode() {
064: return isMasterNode;
065: }
066:
067: /**
068: * @see HASingletonSupportMBean#setElectionPolicy(HASingletonElectionPolicy)
069: */
070: public void setElectionPolicy(HASingletonElectionPolicy mb) {
071: this .mElectionPolicyMB = mb;
072: }
073:
074: /**
075: * @see HASingletonSupportMBean#getElectionPolicy()
076: */
077: public HASingletonElectionPolicy getElectionPolicy() {
078: return this .mElectionPolicyMB;
079: }
080:
081: /**
082: * Gets whether this singleton will stop and restart itself if it is the
083: * master and a cluster merge occurs.
084: * <p/>
085: * A restart allows the service to reset any state that may
086: * have gotten out-of-sync with the rest of the cluster while
087: * the just-merged split was in effect.
088: *
089: * @return <code>true</code> if a restart will occur, <code>false</code>
090: * otherwise
091: */
092: public boolean getRestartOnMerge() {
093: return restartOnMerge;
094: }
095:
096: /**
097: * Sets whether this singleton will stop and restart itself if it is the
098: * master and a cluster merge occurs?
099: * <p/>
100: * A restart allows the service to reset any state that may
101: * have gotten out-of-sync with the rest of the cluster while
102: * the just-merged split was in effect.
103: *
104: * @param restartOnMerge <code>true</code> if a restart should occur,
105: * <code>false</code> otherwise
106: */
107: public void setRestartOnMerge(boolean restartOnMerge) {
108: this .restartOnMerge = restartOnMerge;
109: }
110:
111: // Public --------------------------------------------------------
112:
113: /**
114: * Extending classes should override this method and implement the custom
115: * singleton logic. Only one node in the cluster is the active master.
116: * If the current node is elected for master, this method is invoked.
117: * When another node is elected for master for some reason, the
118: * stopSingleton() method is invokded.
119: * <p>
120: * When the extending class is a stateful singleton, it will
121: * usually use putDistributedState() and getDistributedState() to save in
122: * the cluster environment information that will be needed by the next node
123: * elected for master should the current master node fail.
124: *
125: * @see HASingleton
126: */
127: public void startSingleton() {
128: if (log.isDebugEnabled())
129: log
130: .debug("startSingleton() : elected for master singleton node");
131:
132: // Extending classes will implement the singleton logic here
133: }
134:
135: /**
136: * Extending classes should override this method and implement the custom
137: * singleton logic. Only one node in the cluster is the active master.
138: * If the current node is master and another node is elected for master, this
139: * method is invoked.
140: *
141: * @see HASingleton
142: */
143: public void stopSingleton() {
144: if (log.isDebugEnabled())
145: log
146: .debug("stopSingleton() : another node in the partition (if any) is elected for master");
147:
148: // Extending classes will implement the singleton logic here
149: }
150:
151: /**
152: * When topology changes, a new master is elected based on the result
153: * of the isDRMMasterReplica() call.
154: *
155: * @see HAServiceMBeanSupport#partitionTopologyChanged(List, int)
156: * @see DistributedReplicantManager#isMasterReplica(String);
157: */
158: public void partitionTopologyChanged(List newReplicants,
159: int newViewID) {
160: boolean isElectedNewMaster;
161: if (this .mElectionPolicyMB != null)
162: isElectedNewMaster = this .mElectionPolicyMB
163: .isElectedMaster(this .getPartition());
164: else
165: isElectedNewMaster = isDRMMasterReplica();
166:
167: if (log.isDebugEnabled()) {
168: log.debug("partitionTopologyChanged, isElectedNewMaster="
169: + isElectedNewMaster + ", isMasterNode="
170: + isMasterNode + ", viewID=" + newViewID);
171: }
172:
173: // if this node is already the master, don't bother electing it again
174: if (isElectedNewMaster && isMasterNode) {
175: // JBAS-4229
176: if (restartOnMerge && ClusterMergeStatus.isMergeInProcess()) {
177: restartMaster();
178: }
179: }
180: // just becoming master
181: else if (isElectedNewMaster && !isMasterNode) {
182: makeThisNodeMaster();
183: }
184: // transition from master to slave
185: else if (isMasterNode == true) {
186: _stopOldMaster();
187: }
188: }
189:
190: /**
191: * This method will be invoked twice by the local node
192: * when it stops as well as by the remote
193: */
194: public void _stopOldMaster() {
195: log.debug("_stopOldMaster, isMasterNode=" + isMasterNode);
196:
197: try {
198: // since this is a cluster call, all nodes will hear it
199: // so if the node is not the master, then ignore
200: if (isMasterNode == true) {
201: isMasterNode = false;
202:
203: // notify stopping
204: sendLocalNotification(HASINGLETON_STOPPING_NOTIFICATION);
205:
206: // stop the singleton
207: stopSingleton();
208:
209: // notify stopped
210: sendLocalNotification(HASINGLETON_STOPPED_NOTIFICATION);
211: }
212: } catch (Exception ex) {
213: log
214: .error(
215: "_stopOldMaster failed. Will still try to start new master. "
216: + "You need to examine the reason why the old master wouldn't stop and resolve it. "
217: + "It is bad that the old singleton may still be running while we are starting a new one, "
218: + "so you need to resolve this ASAP.",
219: ex);
220: }
221: }
222:
223: // Protected -----------------------------------------------------
224:
225: protected void makeThisNodeMaster() {
226: try {
227: // stop the old master (if there is one) before starting the new one
228:
229: // ovidiu 09/02/04 - temporary solution for Case 1843, use an asynchronous
230: // distributed call.
231: //callMethodOnPartition("_stopOldMaster", new Object[0], new Class[0]);
232: callAsyncMethodOnPartition("_stopOldMaster", new Object[0],
233: new Class[0]);
234:
235: startNewMaster();
236: } catch (Exception ex) {
237: log
238: .error(
239: "_stopOldMaster failed. New master singleton will not start.",
240: ex);
241: }
242: }
243:
244: protected void startNewMaster() {
245: log.debug("startNewMaster, isMasterNode=" + isMasterNode);
246:
247: isMasterNode = true;
248:
249: // notify starting
250: sendLocalNotification(HASINGLETON_STARTING_NOTIFICATION);
251:
252: // start new master
253: startSingleton();
254:
255: // notify started
256: sendLocalNotification(HASINGLETON_STARTED_NOTIFICATION);
257: }
258:
259: protected void restartMaster() {
260: _stopOldMaster();
261: startNewMaster();
262: }
263:
264: // Private -------------------------------------------------------
265:
266: private void sendLocalNotification(String type) {
267: Notification n = new Notification(type, this,
268: getNextNotificationSequenceNumber());
269: super.sendNotificationToLocalListeners(n);
270: }
271: }
|