PageRenderTime 64ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/jboss-5.1.0/cluster/src/main/org/jboss/ha/framework/server/ClusterPartition.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1438 lines | 1039 code | 177 blank | 222 comment | 132 complexity | dbb09b64c91c21f5d567c78d37673d16 MD5 | raw file
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2008, Red Hat Middleware LLC, and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.ha.framework.server;
  23. import java.io.ByteArrayInputStream;
  24. import java.io.ByteArrayOutputStream;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.io.OutputStream;
  28. import java.io.Serializable;
  29. import java.lang.ref.WeakReference;
  30. import java.net.InetAddress;
  31. import java.text.SimpleDateFormat;
  32. import java.util.ArrayList;
  33. import java.util.Date;
  34. import java.util.HashMap;
  35. import java.util.Map;
  36. import java.util.Set;
  37. import java.util.Vector;
  38. import java.util.concurrent.ConcurrentHashMap;
  39. import java.util.concurrent.CountDownLatch;
  40. import javax.management.JMException;
  41. import javax.management.MBeanServer;
  42. import javax.management.MalformedObjectNameException;
  43. import javax.management.ObjectName;
  44. import javax.naming.Context;
  45. import javax.naming.InitialContext;
  46. import javax.naming.Name;
  47. import javax.naming.NameNotFoundException;
  48. import javax.naming.Reference;
  49. import javax.naming.StringRefAddr;
  50. import org.jboss.bootstrap.spi.util.ServerConfigUtil;
  51. import org.jboss.ha.framework.interfaces.ClusterNode;
  52. import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
  53. import org.jboss.ha.framework.interfaces.HAPartition;
  54. import org.jboss.ha.framework.interfaces.ResponseFilter;
  55. import org.jboss.ha.framework.server.deployers.DefaultHAPartitionDependencyCreator;
  56. import org.jboss.ha.framework.server.deployers.HAPartitionDependencyCreator;
  57. import org.jboss.ha.framework.server.spi.HAPartitionCacheHandler;
  58. import org.jboss.ha.framework.server.spi.ManagedDistributedState;
  59. import org.jboss.invocation.MarshalledValueInputStream;
  60. import org.jboss.invocation.MarshalledValueOutputStream;
  61. import org.jboss.kernel.spi.dependency.KernelController;
  62. import org.jboss.kernel.spi.dependency.KernelControllerContext;
  63. import org.jboss.logging.Logger;
  64. import org.jboss.managed.api.ManagedOperation.Impact;
  65. import org.jboss.managed.api.annotation.ManagementComponent;
  66. import org.jboss.managed.api.annotation.ManagementObject;
  67. import org.jboss.managed.api.annotation.ManagementObjectID;
  68. import org.jboss.managed.api.annotation.ManagementOperation;
  69. import org.jboss.managed.api.annotation.ManagementProperties;
  70. import org.jboss.managed.api.annotation.ManagementProperty;
  71. import org.jboss.managed.api.annotation.ViewUse;
  72. import org.jboss.naming.NonSerializableFactory;
  73. import org.jboss.system.ServiceMBeanSupport;
  74. import org.jboss.util.threadpool.ThreadPool;
  75. import org.jgroups.Address;
  76. import org.jgroups.Channel;
  77. import org.jgroups.ChannelFactory;
  78. import org.jgroups.ExtendedMembershipListener;
  79. import org.jgroups.ExtendedMessageListener;
  80. import org.jgroups.MembershipListener;
  81. import org.jgroups.MergeView;
  82. import org.jgroups.Message;
  83. import org.jgroups.MessageListener;
  84. import org.jgroups.Version;
  85. import org.jgroups.View;
  86. import org.jgroups.blocks.GroupRequest;
  87. import org.jgroups.blocks.MethodCall;
  88. import org.jgroups.blocks.RpcDispatcher;
  89. import org.jgroups.stack.IpAddress;
  90. import org.jgroups.util.Rsp;
  91. import org.jgroups.util.RspList;
  92. /**
  93. * {@link HAPartition} implementation based on a
  94. * <a href="http://www.jgroups.com/">JGroups</a> <code>RpcDispatcher</code>
  95. * and a multiplexed <code>JChannel</code>.
  96. *
  97. * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
  98. * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>.
  99. * @author Scott.Stark@jboss.org
  100. * @author brian.stansberry@jboss.com
  101. * @author Galder ZamarreƱo
  102. * @version $Revision: 89190 $
  103. */
  104. @ManagementObject(componentType=@ManagementComponent(type="MCBean", subtype="HAPartition"),
  105. properties=ManagementProperties.CLASS_AND_EXPLICIT,
  106. classProperties={@ManagementProperty(name="stateString",use={ViewUse.STATISTIC})},
  107. operations={@ManagementOperation(name="create",description="Bring the service to the CREATED state",impact=Impact.WriteOnly),
  108. @ManagementOperation(name="start",description="Bring the service to the STARTED state",impact=Impact.WriteOnly),
  109. @ManagementOperation(name="stop",description="Bring the service to the STOPPED state",impact=Impact.WriteOnly),
  110. @ManagementOperation(name="destroy",description="Bring the service to the DESTROYED state",impact=Impact.WriteOnly)})
  111. public class ClusterPartition
  112. extends ServiceMBeanSupport
  113. implements ExtendedMembershipListener, HAPartition,
  114. AsynchEventHandler.AsynchEventProcessor,
  115. ClusterPartitionMBean
  116. {
  117. public static final String DEFAULT_CACHE_CONFIG = "ha-partition";
  118. private static final byte EOF_VALUE = -1;
  119. private static final byte NULL_VALUE = 0;
  120. private static final byte SERIALIZABLE_VALUE = 1;
  121. // TODO add Streamable support
  122. // private static final byte STREAMABLE_VALUE = 2;
  123. /**
  124. * Returned when an RPC call arrives for a service that isn't registered.
  125. */
  126. public static class NoHandlerForRPC implements Serializable
  127. {
  128. static final long serialVersionUID = -1263095408483622838L;
  129. }
  130. private static class StateStreamEnd implements Serializable
  131. {
  132. /** The serialVersionUID */
  133. private static final long serialVersionUID = -3705345735451504946L;
  134. }
  135. /**
  136. * Used internally when an RPC call requires a custom classloader for unmarshalling
  137. */
  138. private static class HAServiceResponse implements Serializable
  139. {
  140. private static final long serialVersionUID = -6485594652749906437L;
  141. private final String serviceName;
  142. private final byte[] payload;
  143. public HAServiceResponse(String serviceName, byte[] payload)
  144. {
  145. this.serviceName = serviceName;
  146. this.payload = payload;
  147. }
  148. public String getServiceName()
  149. {
  150. return this.serviceName;
  151. }
  152. public byte[] getPayload()
  153. {
  154. return this.payload;
  155. }
  156. }
  157. /**
  158. * Used to connect the channel asynchronously from the thread that calls start().
  159. */
  160. private class ChannelConnectTask implements Runnable
  161. {
  162. private final CountDownLatch latch;
  163. private ChannelConnectTask(CountDownLatch latch)
  164. {
  165. this.latch = latch;
  166. }
  167. public void run()
  168. {
  169. try
  170. {
  171. ClusterPartition.this.channel.connect(ClusterPartition.this.getPartitionName());
  172. }
  173. catch (Exception e)
  174. {
  175. synchronized (ClusterPartition.this.channelLock)
  176. {
  177. ClusterPartition.this.connectException = e;
  178. }
  179. }
  180. finally
  181. {
  182. this.latch.countDown();
  183. }
  184. }
  185. }
  186. // Constants -----------------------------------------------------
  187. // final MethodLookup method_lookup_clos = new MethodLookupClos();
  188. // Attributes ----------------------------------------------------
  189. private HAPartitionCacheHandler cacheHandler;
  190. private String cacheConfigName;
  191. private ChannelFactory channelFactory;
  192. private String stackName;
  193. private String partitionName = ServerConfigUtil.getDefaultPartitionName();
  194. private InetAddress nodeAddress = null;
  195. private long state_transfer_timeout=60000;
  196. private long method_call_timeout=60000;
  197. /** Thread pool used to asynchronously start our channel */
  198. private ThreadPool threadPool;
  199. private final Map<String, Object> rpcHandlers = new ConcurrentHashMap<String, Object>();
  200. private final Map<String, HAPartitionStateTransfer> stateHandlers = new HashMap<String, HAPartitionStateTransfer>();
  201. /** Do we send any membership change notifications synchronously? */
  202. private boolean allowSyncListeners = false;
  203. /** The HAMembershipListener and HAMembershipExtendedListeners */
  204. private final ArrayList<HAMembershipListener> synchListeners = new ArrayList<HAMembershipListener>();
  205. /** The asynch HAMembershipListener and HAMembershipExtendedListeners */
  206. private final ArrayList<HAMembershipListener> asynchListeners = new ArrayList<HAMembershipListener>();
  207. /** The handler used to send membership change notifications asynchronously */
  208. private AsynchEventHandler asynchHandler;
  209. /** The current cluster partition members */
  210. private Vector<ClusterNode> members = null;
  211. private Vector<Address> jgmembers = null;
  212. private final Map<String, WeakReference<ClassLoader>> clmap = new ConcurrentHashMap<String, WeakReference<ClassLoader>>();
  213. private final Vector<String> history = new Vector<String>();
  214. /** The partition members other than this node */
  215. private Vector<ClusterNode> otherMembers = null;
  216. private Vector<Address> jgotherMembers = null;
  217. /** the local JG IP Address */
  218. private Address localJGAddress = null;
  219. /** The cluster transport protocol address string */
  220. private String nodeName;
  221. /** me as a ClusterNode */
  222. private ClusterNode me = null;
  223. /** The JGroups partition channel */
  224. private Channel channel;
  225. /** The cluster replicant manager */
  226. private DistributedReplicantManagerImpl replicantManager;
  227. /** The DistributedState service we manage */
  228. @SuppressWarnings("deprecation")
  229. private org.jboss.ha.framework.interfaces.DistributedState distributedState;
  230. /** The cluster instance log category */
  231. private Logger log = Logger.getLogger(HAPartition.class.getName());;
  232. private Logger clusterLifeCycleLog = Logger.getLogger(HAPartition.class.getName() + ".lifecycle");
  233. /** The current cluster view id */
  234. private long currentViewId = -1;
  235. /** Whether to bind the partition into JNDI */
  236. private boolean bindIntoJndi = true;
  237. private final ThreadGate flushBlockGate = new ThreadGate();
  238. private RpcDispatcher dispatcher = null;
  239. /**
  240. * True if serviceState was initialized during start-up.
  241. */
  242. protected boolean isStateSet = false;
  243. /**
  244. * An exception occuring upon fetch serviceState.
  245. */
  246. private Exception setStateException;
  247. /**
  248. * An exception occuring during channel connect
  249. */
  250. private Exception connectException;
  251. private final Object channelLock = new Object();
  252. private final MessageListenerAdapter messageListener = new MessageListenerAdapter();
  253. private HAPartitionDependencyCreator haPartitionDependencyCreator;
  254. private KernelControllerContext kernelControllerContext;
  255. // Static --------------------------------------------------------
  256. private Channel createChannel()
  257. {
  258. ChannelFactory factory = this.getChannelFactory();
  259. if (factory == null)
  260. {
  261. throw new IllegalStateException("HAPartitionConfig has no JChannelFactory");
  262. }
  263. String stack = this.getChannelStackName();
  264. if (stack == null)
  265. {
  266. throw new IllegalStateException("HAPartitionConfig has no multiplexer stack");
  267. }
  268. try
  269. {
  270. return factory.createMultiplexerChannel(stack, this.getPartitionName());
  271. }
  272. catch (RuntimeException e)
  273. {
  274. throw e;
  275. }
  276. catch (Exception e)
  277. {
  278. throw new RuntimeException("Failure creating multiplexed Channel", e);
  279. }
  280. }
  281. // Constructors --------------------------------------------------
  282. public ClusterPartition()
  283. {
  284. this.logHistory("Partition object created");
  285. }
  286. // ------------------------------------------------------------ ServiceMBean
  287. // ----------------------------------------------------------------- Service
  288. protected void createService() throws Exception
  289. {
  290. if (this.replicantManager == null)
  291. {
  292. this.replicantManager = new DistributedReplicantManagerImpl(this);
  293. }
  294. // registerDRM();
  295. this.setupLoggers(this.getPartitionName());
  296. this.replicantManager.createService();
  297. if (this.distributedState instanceof ManagedDistributedState)
  298. {
  299. ((ManagedDistributedState) this.distributedState).createService();
  300. }
  301. // Create the asynchronous handler for view changes
  302. this.asynchHandler = new AsynchEventHandler(this, "AsynchViewChangeHandler");
  303. // Add a well-known MC alias that other beans can depend on
  304. addCanonicalAlias();
  305. this.log.debug("done initializing partition");
  306. }
  307. protected void startService() throws Exception
  308. {
  309. this.logHistory ("Starting partition");
  310. // Have the handler get the cache
  311. this.cacheHandler.acquireCache();
  312. this.channelFactory = this.cacheHandler.getCacheChannelFactory();
  313. this.stackName = this.cacheHandler.getChannelStackName();
  314. if (this.channel == null || !this.channel.isOpen())
  315. {
  316. this.log.debug("Creating Channel for partition " + this.getPartitionName() +
  317. " using stack " + this.getChannelStackName());
  318. this.channel = this.createChannel();
  319. this.channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE);
  320. this.channel.setOpt(Channel.AUTO_GETSTATE, Boolean.TRUE);
  321. }
  322. this.log.info("Initializing partition " + this.getPartitionName());
  323. this.logHistory ("Initializing partition " + this.getPartitionName());
  324. this.dispatcher = new RpcHandler(this.channel, null, null, new Object(), false);
  325. // Subscribe to events generated by the channel
  326. this.log.debug("setMembershipListener");
  327. this.dispatcher.setMembershipListener(this);
  328. this.log.debug("setMessageListener");
  329. this.dispatcher.setMessageListener(this.messageListener);
  330. this.dispatcher.setRequestMarshaller(new RequestMarshallerImpl());
  331. this.dispatcher.setResponseMarshaller(new ResponseMarshallerImpl());
  332. // Clear any old connectException
  333. this.connectException = null;
  334. CountDownLatch connectLatch = new CountDownLatch(1);
  335. if (this.threadPool == null)
  336. {
  337. this.channel.connect(this.getPartitionName());
  338. connectLatch.countDown();
  339. }
  340. else
  341. {
  342. // Do the channel connect in another thread while this
  343. // thread starts the cache and does that channel connect
  344. ChannelConnectTask task = new ChannelConnectTask(connectLatch);
  345. this.threadPool.run(task);
  346. }
  347. this.cacheHandler.startCache();
  348. try
  349. {
  350. // This will block waiting for any async channel connect above
  351. connectLatch.await();
  352. if (this.connectException != null)
  353. {
  354. throw this.connectException;
  355. }
  356. this.log.debug("Get current members");
  357. this.waitForView();
  358. // get current JG group properties
  359. this.log.debug("get nodeName");
  360. this.localJGAddress = this.channel.getLocalAddress();
  361. this.me = new ClusterNodeImpl((IpAddress) this.localJGAddress);
  362. this.nodeName = this.me.getName();
  363. this.verifyNodeIsUnique();
  364. this.fetchState();
  365. this.replicantManager.startService();
  366. if (this.distributedState instanceof ManagedDistributedState)
  367. {
  368. ((ManagedDistributedState) this.distributedState).startService();
  369. }
  370. // Start the asynch listener handler thread
  371. this.asynchHandler.start();
  372. // Register with the service locator
  373. HAPartitionLocator.getHAPartitionLocator().registerHAPartition(this);
  374. // Bind ourself in the public JNDI space if configured to do so
  375. if (this.bindIntoJndi)
  376. {
  377. Context ctx = new InitialContext();
  378. this.bind(HAPartitionLocator.getStandardJndiBinding(this.getPartitionName()),
  379. this, ClusterPartition.class, ctx);
  380. this.log.debug("Bound in JNDI under /HAPartition/" + this.getPartitionName());
  381. }
  382. }
  383. catch (Throwable t)
  384. {
  385. this.log.debug("Caught exception after channel connected; closing channel -- " + t.getLocalizedMessage());
  386. this.channel.close();
  387. this.channel = null;
  388. throw (t instanceof Exception) ? (Exception) t : new RuntimeException(t);
  389. }
  390. }
  391. protected void stopService() throws Exception
  392. {
  393. this.logHistory ("Stopping partition");
  394. this.log.info("Stopping partition " + this.getPartitionName());
  395. try
  396. {
  397. this.asynchHandler.stop();
  398. }
  399. catch( Exception e)
  400. {
  401. this.log.warn("Failed to stop asynchHandler", e);
  402. }
  403. if (this.distributedState instanceof ManagedDistributedState)
  404. {
  405. ((ManagedDistributedState) this.distributedState).stopService();
  406. }
  407. this.replicantManager.stopService();
  408. try
  409. {
  410. this.cacheHandler.releaseCache();
  411. }
  412. catch (Exception e)
  413. {
  414. this.log.error("cache release failed", e);
  415. }
  416. // NR 200505 : [JBCLUSTER-38] replace channel.close() by a disconnect and
  417. // add the destroyPartition() step
  418. try
  419. {
  420. if (this.channel != null && this.channel.isConnected())
  421. {
  422. this.channel.disconnect();
  423. }
  424. }
  425. catch (Exception e)
  426. {
  427. this.log.error("channel disconnection failed", e);
  428. }
  429. if (this.bindIntoJndi)
  430. {
  431. String boundName = HAPartitionLocator.getStandardJndiBinding(this.getPartitionName());
  432. InitialContext ctx = null;
  433. try
  434. {
  435. // the following statement fails when the server is being shut down (07/19/2007)
  436. ctx = new InitialContext();
  437. ctx.unbind(boundName);
  438. }
  439. catch (Exception e) {
  440. this.log.error("partition unbind operation failed", e);
  441. }
  442. finally
  443. {
  444. if (ctx != null)
  445. {
  446. ctx.close();
  447. }
  448. }
  449. NonSerializableFactory.unbind(boundName);
  450. }
  451. HAPartitionLocator.getHAPartitionLocator().deregisterHAPartition(this);
  452. this.log.info("Partition " + this.getPartitionName() + " stopped.");
  453. }
  454. protected void destroyService() throws Exception
  455. {
  456. this.log.debug("Destroying HAPartition: " + this.getPartitionName());
  457. removeCanonicalAlias();
  458. if (this.distributedState instanceof ManagedDistributedState)
  459. {
  460. ((ManagedDistributedState) this.distributedState).destroyService();
  461. }
  462. this.replicantManager.destroyService();
  463. // unregisterDRM();
  464. try
  465. {
  466. if (this.channel != null && this.channel.isOpen())
  467. {
  468. this.channel.close();
  469. }
  470. }
  471. catch (Exception e)
  472. {
  473. this.log.error("Closing channel failed", e);
  474. }
  475. this.log.info("Partition " + this.getPartitionName() + " destroyed.");
  476. }
  477. /**
  478. * Adds an alias to our controller context -- the concatenation of
  479. * {@link #getAliasPrefix()} and {@link #getPartitionName()}.
  480. * This mechanism allows Ejb2HAPartitionDependencyDeployer to add
  481. * dependencies to deployments based on the partition name specified in
  482. * their metadata, without needing to know the bean name of this partition.
  483. */
  484. private void addCanonicalAlias()
  485. {
  486. if (kernelControllerContext != null)
  487. {
  488. KernelController kc = (KernelController) kernelControllerContext.getController();
  489. String aliasName = getHaPartitionDependencyCreator().getHAPartitionDependencyName(this.partitionName);
  490. try
  491. {
  492. kc.addAlias(aliasName, kernelControllerContext.getName());
  493. }
  494. catch (Throwable t)
  495. {
  496. log.error("Failed adding alias " + aliasName + " to context " + kernelControllerContext.getName(), t);
  497. }
  498. }
  499. }
  500. /**
  501. * Removes the alias created in {@link #addCanonicalAlias()}
  502. */
  503. private void removeCanonicalAlias()
  504. {
  505. if (kernelControllerContext != null)
  506. {
  507. KernelController kc = (KernelController) kernelControllerContext.getController();
  508. String aliasName = getHaPartitionDependencyCreator().getHAPartitionDependencyName(this.partitionName);
  509. Set<Object> aliases = kernelControllerContext.getAliases();
  510. if (aliases != null && aliases.contains(aliasName))
  511. {
  512. try
  513. {
  514. kc.removeAlias(aliasName);
  515. }
  516. catch (Throwable t)
  517. {
  518. log.error("Failed removing alias " + aliasName + " from context " + kernelControllerContext.getName(), t);
  519. }
  520. }
  521. }
  522. }
  523. // ---------------------------------------------------------- State Transfer
  524. protected void fetchState() throws Exception
  525. {
  526. this.log.info("Fetching serviceState (will wait for " + this.getStateTransferTimeout() +
  527. " milliseconds):");
  528. long start, stop;
  529. this.isStateSet = false;
  530. start = System.currentTimeMillis();
  531. boolean rc = this.channel.getState(null, this.getStateTransferTimeout());
  532. if (rc)
  533. {
  534. synchronized (this.channelLock)
  535. {
  536. while (!this.isStateSet)
  537. {
  538. if (this.setStateException != null)
  539. {
  540. throw this.setStateException;
  541. }
  542. try
  543. {
  544. this.channelLock.wait();
  545. }
  546. catch (InterruptedException iex)
  547. {
  548. }
  549. }
  550. }
  551. stop = System.currentTimeMillis();
  552. this.log.info("serviceState was retrieved successfully (in " + (stop - start) + " milliseconds)");
  553. }
  554. else
  555. {
  556. // No one provided us with serviceState.
  557. // We need to find out if we are the coordinator, so we must
  558. // block until viewAccepted() is called at least once
  559. synchronized (this.members)
  560. {
  561. while (this.members.size() == 0)
  562. {
  563. this.log.debug("waiting on viewAccepted()");
  564. try
  565. {
  566. this.members.wait();
  567. }
  568. catch (InterruptedException iex)
  569. {
  570. }
  571. }
  572. }
  573. if (this.isCurrentNodeCoordinator())
  574. {
  575. this.log.info("State could not be retrieved (we are the first member in group)");
  576. }
  577. else
  578. {
  579. throw new IllegalStateException("Initial serviceState transfer failed: " +
  580. "Channel.getState() returned false");
  581. }
  582. }
  583. }
  584. private void getStateInternal(OutputStream stream) throws IOException
  585. {
  586. MarshalledValueOutputStream mvos = null; // don't create until we know we need it
  587. for (Map.Entry<String, HAPartitionStateTransfer> entry: this.stateHandlers.entrySet())
  588. {
  589. HAPartitionStateTransfer subscriber = entry.getValue();
  590. this.log.debug("getState for " + entry.getKey());
  591. Object state = subscriber.getCurrentState();
  592. if (state != null)
  593. {
  594. if (mvos == null)
  595. {
  596. // This is our first write, so need to write the header first
  597. stream.write(SERIALIZABLE_VALUE);
  598. mvos = new MarshalledValueOutputStream(stream);
  599. }
  600. mvos.writeObject(entry.getKey());
  601. mvos.writeObject(state);
  602. }
  603. }
  604. if (mvos == null)
  605. {
  606. // We never wrote any serviceState, so write the NULL header
  607. stream.write(NULL_VALUE);
  608. }
  609. else
  610. {
  611. mvos.writeObject(new StateStreamEnd());
  612. mvos.flush();
  613. mvos.close();
  614. }
  615. }
  616. private void setStateInternal(InputStream stream) throws IOException, ClassNotFoundException
  617. {
  618. byte type = (byte) stream.read();
  619. if (type == EOF_VALUE)
  620. {
  621. this.log.debug("serviceState stream is empty");
  622. return;
  623. }
  624. else if (type == NULL_VALUE)
  625. {
  626. this.log.debug("serviceState is null");
  627. return;
  628. }
  629. long used_mem_before, used_mem_after;
  630. Runtime rt=Runtime.getRuntime();
  631. used_mem_before=rt.totalMemory() - rt.freeMemory();
  632. MarshalledValueInputStream mvis = new MarshalledValueInputStream(stream);
  633. while (true)
  634. {
  635. Object obj = mvis.readObject();
  636. if (obj instanceof StateStreamEnd)
  637. {
  638. break;
  639. }
  640. String key = (String) obj;
  641. this.log.debug("setState for " + key);
  642. Object someState = mvis.readObject();
  643. HAPartitionStateTransfer subscriber = this.stateHandlers.get(key);
  644. if (subscriber != null)
  645. {
  646. try
  647. {
  648. subscriber.setCurrentState((Serializable)someState);
  649. }
  650. catch (Exception e)
  651. {
  652. // Don't let issues with one subscriber affect others
  653. // unless it is DRM, which is really an internal function
  654. // of the HAPartition
  655. // FIXME remove this once DRM is JBC-based
  656. if (DistributedReplicantManagerImpl.SERVICE_NAME.equals(key))
  657. {
  658. if (e instanceof RuntimeException)
  659. {
  660. throw (RuntimeException) e;
  661. }
  662. throw new RuntimeException(e);
  663. }
  664. this.log.error("Caught exception setting serviceState to " + subscriber, e);
  665. }
  666. }
  667. else
  668. {
  669. this.log.debug("There is no stateHandler for: " + key);
  670. }
  671. }
  672. try
  673. {
  674. stream.close();
  675. }
  676. catch(Exception e)
  677. {
  678. this.log.error("Caught exception closing serviceState stream", e);
  679. }
  680. used_mem_after=rt.totalMemory() - rt.freeMemory();
  681. this.log.debug("received serviceState; expanded memory by " +
  682. (used_mem_after - used_mem_before) + " bytes (used memory before: " + used_mem_before +
  683. ", used memory after: " + used_mem_after + ")");
  684. }
  685. private void recordSetStateFailure(Throwable t)
  686. {
  687. this.log.error("failed setting serviceState", t);
  688. if (t instanceof Exception)
  689. {
  690. this.setStateException = (Exception) t;
  691. }
  692. else
  693. {
  694. this.setStateException = new Exception(t);
  695. }
  696. }
  697. private void notifyChannelLock()
  698. {
  699. synchronized (this.channelLock)
  700. {
  701. this.channelLock.notifyAll();
  702. }
  703. }
  704. // org.jgroups.MembershipListener implementation ----------------------------------------------
  705. public void suspect(org.jgroups.Address suspected_mbr)
  706. {
  707. this.logHistory ("Node suspected: " + (suspected_mbr==null?"null":suspected_mbr.toString()));
  708. if (this.isCurrentNodeCoordinator ())
  709. {
  710. this.clusterLifeCycleLog.info ("Suspected member: " + suspected_mbr);
  711. }
  712. else
  713. {
  714. this.log.info("Suspected member: " + suspected_mbr);
  715. }
  716. }
  717. public void block()
  718. {
  719. this.flushBlockGate.close();
  720. this.log.debug("Block processed at " + this.me);
  721. }
  722. public void unblock()
  723. {
  724. this.flushBlockGate.open();
  725. this.log.debug("Unblock processed at " + this.me);
  726. }
  727. /** Notification of a cluster view change. This is done from the JG protocol
  728. * handlder thread and we must be careful to not unduly block this thread.
  729. * Because of this there are two types of listeners, synchronous and
  730. * asynchronous. The synchronous listeners are messaged with the view change
  731. * event using the calling thread while the asynchronous listeners are
  732. * messaged using a seperate thread.
  733. *
  734. * @param newView
  735. */
  736. public void viewAccepted(View newView)
  737. {
  738. try
  739. {
  740. // we update the view id
  741. this.currentViewId = newView.getVid().getId();
  742. // Keep a list of other members only for "exclude-self" RPC calls
  743. this.jgotherMembers = (Vector<Address>)newView.getMembers().clone();
  744. this.jgotherMembers.remove (this.channel.getLocalAddress());
  745. this.otherMembers = this.translateAddresses (this.jgotherMembers); // TRANSLATE!
  746. Vector<ClusterNode> translatedNewView = this.translateAddresses ((Vector<Address>)newView.getMembers().clone());
  747. this.logHistory ("New view: " + translatedNewView + " with viewId: " + this.currentViewId +
  748. " (old view: " + this.members + " )");
  749. // Save the previous view and make a copy of the new view
  750. Vector<ClusterNode> oldMembers = this.members;
  751. Vector<Address> newjgMembers = (Vector<Address>)newView.getMembers().clone();
  752. Vector<ClusterNode> newMembers = this.translateAddresses(newjgMembers); // TRANSLATE
  753. this.members = newMembers;
  754. this.jgmembers = newjgMembers;
  755. if (oldMembers == null)
  756. {
  757. // Initial viewAccepted
  758. this.log.debug("ViewAccepted: initial members set for partition " + this.getPartitionName() + ": " +
  759. this.currentViewId + " (" + this.members + ")");
  760. this.log.info("Number of cluster members: " + this.members.size());
  761. for(int m = 0; m > this.members.size(); m ++)
  762. {
  763. Object node = this.members.get(m);
  764. this.log.debug(node);
  765. }
  766. this.log.info ("Other members: " + this.otherMembers.size ());
  767. // Wake up the deployer thread blocking in waitForView
  768. this.notifyChannelLock();
  769. return;
  770. }
  771. int difference = newMembers.size() - oldMembers.size();
  772. if (this.isCurrentNodeCoordinator ())
  773. {
  774. this.clusterLifeCycleLog.info ("New cluster view for partition " + this.getPartitionName() + " (id: " +
  775. this.currentViewId + ", delta: " + difference + ") : " + this.members);
  776. }
  777. else
  778. {
  779. this.log.info("New cluster view for partition " + this.getPartitionName() + ": " +
  780. this.currentViewId + " (" + this.members + " delta: " + difference + ")");
  781. }
  782. // Build a ViewChangeEvent for the asynch listeners
  783. ViewChangeEvent event = new ViewChangeEvent();
  784. event.viewId = this.currentViewId;
  785. event.allMembers = translatedNewView;
  786. event.deadMembers = this.getDeadMembers(oldMembers, event.allMembers);
  787. event.newMembers = this.getNewMembers(oldMembers, event.allMembers);
  788. event.originatingGroups = null;
  789. // if the new view occurs because of a merge, we first inform listeners of the merge
  790. if(newView instanceof MergeView)
  791. {
  792. MergeView mergeView = (MergeView) newView;
  793. event.originatingGroups = mergeView.getSubgroups();
  794. }
  795. this.log.debug("membership changed from " + oldMembers.size() + " to " + event.allMembers.size());
  796. // Put the view change to the asynch queue
  797. this.asynchHandler.queueEvent(event);
  798. // Broadcast the new view to the synchronous view change listeners
  799. if (this.allowSyncListeners)
  800. {
  801. this.notifyListeners(this.synchListeners, event.viewId, event.allMembers,
  802. event.deadMembers, event.newMembers, event.originatingGroups);
  803. }
  804. }
  805. catch (Exception ex)
  806. {
  807. this.log.error("ViewAccepted failed", ex);
  808. }
  809. }
  810. private void waitForView() throws Exception
  811. {
  812. synchronized (this.channelLock)
  813. {
  814. if (this.members == null)
  815. {
  816. if (this.connectException != null)
  817. {
  818. throw this.connectException;
  819. }
  820. try
  821. {
  822. this.channelLock.wait(this.getMethodCallTimeout());
  823. }
  824. catch (InterruptedException iex)
  825. {
  826. }
  827. if (this.connectException != null)
  828. {
  829. throw this.connectException;
  830. }
  831. if (this.members == null)
  832. {
  833. throw new IllegalStateException("No view received from Channel");
  834. }
  835. }
  836. }
  837. }
  838. // HAPartition implementation ----------------------------------------------
  839. @ManagementProperty(use={ViewUse.STATISTIC}, description="The identifier for this node in cluster topology views")
  840. public String getNodeName()
  841. {
  842. return this.nodeName;
  843. }
  844. @ManagementProperty(use={ViewUse.CONFIGURATION}, description="The partition's name")
  845. @ManagementObjectID(type="HAPartition")
  846. public String getPartitionName()
  847. {
  848. return this.partitionName;
  849. }
  850. public void setPartitionName(String newName)
  851. {
  852. this.partitionName = newName;
  853. }
  854. public DistributedReplicantManager getDistributedReplicantManager()
  855. {
  856. return this.replicantManager;
  857. }
  858. @SuppressWarnings("deprecation")
  859. public org.jboss.ha.framework.interfaces.DistributedState getDistributedStateService()
  860. {
  861. return this.distributedState;
  862. }
  863. @ManagementProperty(use={ViewUse.STATISTIC}, description="Identifier for the current topology view")
  864. public long getCurrentViewId()
  865. {
  866. return this.currentViewId;
  867. }
  868. @ManagementProperty(use={ViewUse.STATISTIC}, description="The current cluster topology view")
  869. public Vector<String> getCurrentView()
  870. {
  871. Vector<String> result = new Vector<String>(this.members.size());
  872. for (ClusterNode member: this.members)
  873. {
  874. result.add(member.getName());
  875. }
  876. return result;
  877. }
  878. public ClusterNode[] getClusterNodes ()
  879. {
  880. synchronized (this.members)
  881. {
  882. return this.members.toArray(new ClusterNode[this.members.size()]);
  883. }
  884. }
  885. public ClusterNode getClusterNode ()
  886. {
  887. return this.me;
  888. }
  889. @ManagementProperty(use={ViewUse.STATISTIC}, description="Whether this node is acting as the group coordinator for the partition")
  890. public boolean isCurrentNodeCoordinator ()
  891. {
  892. if(this.members == null || this.members.size() == 0 || this.me == null)
  893. {
  894. return false;
  895. }
  896. return this.members.elementAt (0).equals (this.me);
  897. }
  898. // ***************************
  899. // ***************************
  900. // RPC multicast communication
  901. // ***************************
  902. // ***************************
  903. public void registerRPCHandler(String objName, Object subscriber)
  904. {
  905. this.rpcHandlers.put(objName, subscriber);
  906. }
  907. public void registerRPCHandler(String objName, Object subscriber, ClassLoader classloader)
  908. {
  909. this.registerRPCHandler(objName, subscriber);
  910. this.clmap.put(objName, new WeakReference<ClassLoader>(classloader));
  911. }
  912. public void unregisterRPCHandler(String objName, Object subscriber)
  913. {
  914. this.rpcHandlers.remove(objName);
  915. this.clmap.remove(objName);
  916. }
  917. /**
  918. * This function is an abstraction of RpcDispatcher.
  919. */
  920. @SuppressWarnings("unchecked")
  921. public ArrayList callMethodOnCluster(String objName, String methodName,
  922. Object[] args, Class[] types, boolean excludeSelf) throws Exception
  923. {
  924. return this.callMethodOnCluster(objName, methodName, args, types, excludeSelf, null);
  925. }
  926. @SuppressWarnings("unchecked")
  927. public ArrayList callMethodOnCluster(String objName, String methodName,
  928. Object[] args, Class[] types, boolean excludeSelf, ResponseFilter filter) throws Exception
  929. {
  930. return this.callMethodOnCluster(objName, methodName, args, types, excludeSelf, this.getMethodCallTimeout(), filter);
  931. }
  932. @SuppressWarnings("unchecked")
  933. public ArrayList callMethodOnCluster(String objName, String methodName,
  934. Object[] args, Class[] types, boolean excludeSelf, long methodTimeout, ResponseFilter filter) throws Exception
  935. {
  936. RspList rsp = null;
  937. boolean trace = this.log.isTraceEnabled();
  938. MethodCall m = new MethodCall(objName + "." + methodName, args, types);
  939. RspFilterAdapter rspFilter = filter == null ? null : new RspFilterAdapter(filter);
  940. if(this.channel.flushSupported())
  941. {
  942. this.flushBlockGate.await(this.getStateTransferTimeout());
  943. }
  944. if (excludeSelf)
  945. {
  946. if( trace )
  947. {
  948. this.log.trace("callMethodOnCluster(true), objName="+objName
  949. +", methodName="+methodName+", members="+this.jgotherMembers);
  950. }
  951. rsp = this.dispatcher.callRemoteMethods(this.jgotherMembers, m, GroupRequest.GET_ALL, methodTimeout, false, false, rspFilter);
  952. }
  953. else
  954. {
  955. if( trace )
  956. {
  957. this.log.trace("callMethodOnCluster(false), objName="+objName
  958. +", methodName="+methodName+", members="+this.members);
  959. }
  960. rsp = this.dispatcher.callRemoteMethods(null, m, GroupRequest.GET_ALL, methodTimeout, false, false, rspFilter);
  961. }
  962. return this.processResponseList(rsp, trace);
  963. }
  964. /**
  965. * Calls method on Cluster coordinator node only. The cluster coordinator node is the first node to join the
  966. * cluster.
  967. * and is replaced
  968. * @param objName
  969. * @param methodName
  970. * @param args
  971. * @param types
  972. * @param excludeSelf
  973. * @return an array of responses from remote nodes
  974. * @throws Exception
  975. */
  976. @SuppressWarnings("unchecked")
  977. public ArrayList callMethodOnCoordinatorNode(String objName, String methodName,
  978. Object[] args, Class[] types,boolean excludeSelf) throws Exception
  979. {
  980. return this.callMethodOnCoordinatorNode(objName,methodName,args,types,excludeSelf, this.getMethodCallTimeout());
  981. }
  982. /**
  983. * Calls method on Cluster coordinator node only. The cluster coordinator node is the first node to join the
  984. * cluster.
  985. * and is replaced
  986. * @param objName
  987. * @param methodName
  988. * @param args
  989. * @param types
  990. * @param excludeSelf
  991. * @param methodTimeout
  992. * @return an array of responses from remote nodes
  993. * @throws Exception
  994. */
  995. @SuppressWarnings("unchecked")
  996. public ArrayList callMethodOnCoordinatorNode(String objName, String methodName,
  997. Object[] args, Class[] types,boolean excludeSelf, long methodTimeout) throws Exception
  998. {
  999. boolean trace = this.log.isTraceEnabled();
  1000. MethodCall m = new MethodCall(objName + "." + methodName, args, types);
  1001. if( trace )
  1002. {
  1003. this.log.trace("callMethodOnCoordinatorNode(false), objName="+objName
  1004. +", methodName="+methodName);
  1005. }
  1006. // the first cluster view member is the coordinator
  1007. Vector<Address> coordinatorOnly = new Vector<Address>();
  1008. // If we are the coordinator, only call ourself if 'excludeSelf' is false
  1009. if (false == this.isCurrentNodeCoordinator () ||
  1010. false == excludeSelf)
  1011. {
  1012. coordinatorOnly.addElement(this.jgmembers.elementAt(0));
  1013. }
  1014. RspList rsp = this.dispatcher.callRemoteMethods(coordinatorOnly, m, GroupRequest.GET_ALL, methodTimeout);
  1015. return this.processResponseList(rsp, trace);
  1016. }
  1017. /**
  1018. * Calls method synchrounously on target node only.
  1019. * @param serviceName Name of the target service name on which calls are de-multiplexed
  1020. * @param methodName name of the Java method to be called on remote services
  1021. * @param args array of Java Object representing the set of parameters to be
  1022. * given to the remote method
  1023. * @param types The types of the parameters
  1024. * node of the partition or only on remote nodes
  1025. * @param targetNode is the target of the call
  1026. * @return the value returned by the target method
  1027. * @throws Exception Throws if a communication exception occurs
  1028. */
  1029. @SuppressWarnings("unchecked")
  1030. public Object callMethodOnNode(String serviceName, String methodName,
  1031. Object[] args, Class[] types, long methodTimeout, ClusterNode targetNode) throws Throwable
  1032. {
  1033. if (!(targetNode instanceof ClusterNodeImpl))
  1034. {
  1035. throw new IllegalArgumentException("targetNode " + targetNode + " is not an instance of " +
  1036. ClusterNodeImpl.class + " -- only targetNodes provided by this HAPartition should be used");
  1037. }
  1038. boolean trace = this.log.isTraceEnabled();
  1039. MethodCall m = new MethodCall(serviceName + "." + methodName, args, types);
  1040. if( trace )
  1041. {
  1042. this.log.trace("callMethodOnNode( objName="+serviceName
  1043. +", methodName="+methodName);
  1044. }
  1045. Object rc = this.dispatcher.callRemoteMethod(((ClusterNodeImpl)targetNode).getOriginalJGAddress(), m, GroupRequest.GET_FIRST, methodTimeout);
  1046. if (rc != null)
  1047. {
  1048. Object item = rc;
  1049. if (item instanceof Rsp)
  1050. {
  1051. Rsp response = (Rsp) item;
  1052. // Only include received responses
  1053. boolean wasReceived = response.wasReceived();
  1054. if( wasReceived == true )
  1055. {
  1056. item = response.getValue();
  1057. if (!(item instanceof NoHandlerForRPC))
  1058. {
  1059. rc = item;
  1060. }
  1061. }
  1062. else if( trace )
  1063. {
  1064. this.log.trace("Ignoring non-received response: "+response);
  1065. }
  1066. }
  1067. else
  1068. {
  1069. if (!(item instanceof NoHandlerForRPC))
  1070. {
  1071. rc = item;
  1072. }
  1073. else if( trace )
  1074. {
  1075. this.log.trace("Ignoring NoHandlerForRPC");
  1076. }
  1077. }
  1078. }
  1079. return rc;
  1080. }
  1081. /**
  1082. * Calls method on target node only.
  1083. * @param serviceName Name of the target service name on which calls are de-multiplexed
  1084. * @param methodName name of the Java method to be called on remote services
  1085. * @param args array of Java Object representing the set of parameters to be
  1086. * given to the remote method
  1087. * @param types The types of the parameters
  1088. * node of the partition or only on remote nodes
  1089. * @param targetNode is the target of the call
  1090. * @return none
  1091. * @throws Exception Throws if a communication exception occurs
  1092. */
  1093. @SuppressWarnings("unchecked")
  1094. public void callAsyncMethodOnNode(String serviceName, String methodName,
  1095. Object[] args, Class[] types, long methodTimeout, ClusterNode targetNode) throws Throwable
  1096. {
  1097. if (!(targetNode instanceof ClusterNodeImpl))
  1098. {
  1099. throw new IllegalArgumentException("targetNode " + targetNode + " is not an instance of " +
  1100. ClusterNodeImpl.class + " -- only targetNodes provided by this HAPartition should be used");
  1101. }
  1102. boolean trace = this.log.isTraceEnabled();
  1103. MethodCall m = new MethodCall(serviceName + "." + methodName, args, types);
  1104. if( trace )
  1105. {
  1106. this.log.trace("callAsyncMethodOnNode( objName="+serviceName
  1107. +", methodName="+methodName);
  1108. }
  1109. this.dispatcher.callRemoteMethod(((ClusterNodeImpl)targetNode).getOriginalJGAddress(), m, GroupRequest.GET_NONE, methodTimeout);
  1110. }
  1111. private ArrayList<Object> processResponseList(RspList rsp, boolean trace)
  1112. {
  1113. ArrayList<Object> rtn = new ArrayList<Object>();
  1114. if (rsp != null)
  1115. {
  1116. for (Object item : rsp.values())
  1117. {
  1118. if (item instanceof Rsp)
  1119. {
  1120. Rsp response = (Rsp) item;
  1121. // Only include received responses
  1122. boolean wasReceived = response.wasReceived();
  1123. if( wasReceived == true )
  1124. {
  1125. item = response.getValue();
  1126. if (!(item instanceof NoHandlerForRPC))
  1127. {
  1128. rtn.add(item);
  1129. }
  1130. }
  1131. else if( trace )
  1132. {
  1133. this.log.trace("Ignoring non-received response: "+response);
  1134. }
  1135. }
  1136. else
  1137. {
  1138. if (!(item instanceof NoHandlerForRPC))
  1139. {
  1140. rtn.add(item);
  1141. }
  1142. else if( trace )
  1143. {
  1144. this.log.trace("Ignoring NoHandlerForRPC");
  1145. }
  1146. }
  1147. }
  1148. }
  1149. return rtn;
  1150. }
  1151. /**
  1152. * This function is an abstraction of RpcDispatcher for asynchronous messages
  1153. */
  1154. @SuppressWarnings("unchecked")
  1155. public void callAsynchMethodOnCluster(String objName, String methodName,
  1156. Object[] args, Class[] types, boolean excludeSelf) throws Exception
  1157. {
  1158. boolean trace = this.log.isTraceEnabled();
  1159. MethodCall m = new MethodCall(objName + "." + methodName, args, types);
  1160. if(this.channel.flushSupported())
  1161. {
  1162. this.flushBlockGate.await(this.getStateTransferTimeout());
  1163. }
  1164. if (excludeSelf)
  1165. {
  1166. if( trace )
  1167. {
  1168. this.log.trace("callAsynchMethodOnCluster(true), objName="+objName
  1169. +", methodName="+methodName+", members="+this.jgotherMembers);
  1170. }
  1171. this.dispatcher.callRemoteMethods(this.jgotherMembers, m, GroupRequest.GET_NONE, this.getMethodCallTimeout());
  1172. }
  1173. else
  1174. {
  1175. if( trace )
  1176. {
  1177. this.log.trace("callAsynchMethodOnCluster(false), objName="+objName
  1178. +", methodName="+methodName+", members="+this.members);
  1179. }
  1180. this.dispatcher.callRemoteMethods(null, m, GroupRequest.GET_NONE, this.getMethodCallTimeout());
  1181. }
  1182. }
  1183. // *************************
  1184. // *************************
  1185. // State transfer management
  1186. // *************************
  1187. // *************************
  1188. public void subscribeToStateTransferEvents(String objectName, HAPartitionStateTransfer subscriber)
  1189. {
  1190. this.stateHandlers.put(objectName, subscriber);
  1191. }
  1192. public void unsubscribeFromStateTransferEvents(String objectName, HAPartitionStateTransfer subscriber)
  1193. {
  1194. this.stateHandlers.remove(objectName);
  1195. }
  1196. // *************************
  1197. // *************************
  1198. // Group Membership listeners
  1199. // *************************
  1200. // *************************
  1201. public void registerMembershipListener(HAMembershipListener listener)
  1202. {
  1203. boolean isAsynch = (this.allowSyncListeners == false)
  1204. || (listener instanceof AsynchHAMembershipListener)
  1205. || (listener instanceof AsynchHAMembershipExtendedListener);
  1206. if( isAsynch ) {
  1207. synchronized(this.asynchListeners) {
  1208. this.asynchListeners.add(listener);
  1209. }
  1210. }
  1211. else {
  1212. synchronized(this.synchListeners) {
  1213. this.synchListeners.add(listener);
  1214. }
  1215. }
  1216. }
  1217. public void unregisterMembershipListener(HAMembershipListener listener)
  1218. {
  1219. boolean isAsynch = (this.allowSyncListeners == false)
  1220. || (listener instanceof AsynchHAMembershipListener)
  1221. || (listener instanceof AsynchHAMembershipExtendedListener);
  1222. if( isAsynch ) {
  1223. synchronized(this.asynchListeners) {
  1224. this.asynchListeners.remove(listener);
  1225. }
  1226. }
  1227. else {
  1228. synchronized(this.synchListeners) {
  1229. this.synchListeners.remove(listener);
  1230. }
  1231. }
  1232. }
  1233. @ManagementProperty(use={ViewUse.CONFIGURATION, ViewUse.RUNTIME},
  1234. description="Whether to allow synchronous notifications of topology changes")
  1235. public boolean getAllowSynchronousMembershipNotifications()
  1236. {
  1237. return this.allowSyncListeners;
  1238. }
  1239. /**
  1240. * Sets whether this partition will synchronously notify any
  1241. * HAPartition.HAMembershipListener of membership changes using the
  1242. * calling thread from the underlying group communications layer
  1243. * (e.g. JGroups).
  1244. *
  1245. * @param allowSync <code>true</code> if registered listeners that don't
  1246. * implement <code>AsynchHAMembershipExtendedListener</code> or
  1247. * <code>AsynchHAMembershipListener</code> should be notified
  1248. * synchronously of membership changes; <code>false</code> if
  1249. * those listeners can be notified asynchronously. Default
  1250. * is <code>false</code>.
  1251. */
  1252. public void setAllowSynchronousMembershipNotifications(boolean allowSync)
  1253. {
  1254. this.allowSyncListeners = allowSync;
  1255. }
  1256. // AsynchEventHandler.AsynchEventProcessor -----------------------
  1257. public void processEvent(Object event)
  1258. {
  1259. ViewChangeEvent vce = (ViewChangeEvent) event;
  1260. this.notifyListeners(this.asynchListeners, vce.viewId, vce.allMembers,
  1261. vce.deadMembers, vce.newMembers, vce.originatingGroups);