| /* |
| * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2021 Contributors to the Eclipse Foundation |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package org.glassfish.gms; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.logging.Logger; |
| import org.glassfish.api.logging.LogLevel; |
| |
| import jakarta.inject.Inject; |
| import jakarta.inject.Named; |
| |
| import org.glassfish.api.admin.ServerEnvironment; |
| import org.glassfish.api.event.EventListener; |
| import org.glassfish.api.event.EventTypes; |
| import org.glassfish.api.event.Events; |
| import org.glassfish.gms.bootstrap.GMSAdapter; |
| import org.glassfish.gms.bootstrap.HealthHistory; |
| import org.glassfish.hk2.api.PerLookup; |
| import org.glassfish.hk2.api.PostConstruct; |
| import org.glassfish.hk2.api.ServiceLocator; |
| import org.glassfish.logging.annotation.LogMessageInfo; |
| import org.glassfish.logging.annotation.LogMessagesResourceBundle; |
| import org.glassfish.logging.annotation.LoggerInfo; |
| import org.jvnet.hk2.annotations.Service; |
| import org.jvnet.hk2.config.Dom; |
| import org.jvnet.hk2.config.types.Property; |
| |
| import com.sun.enterprise.config.serverbeans.Cluster; |
| import com.sun.enterprise.config.serverbeans.Clusters; |
| import com.sun.enterprise.config.serverbeans.Config; |
| import com.sun.enterprise.config.serverbeans.Domain; |
| import com.sun.enterprise.config.serverbeans.Node; |
| import com.sun.enterprise.config.serverbeans.Nodes; |
| import com.sun.enterprise.config.serverbeans.Server; |
| import com.sun.enterprise.config.serverbeans.ServerRef; |
| import com.sun.enterprise.config.serverbeans.Servers; |
| import com.sun.enterprise.ee.cms.core.AliveAndReadySignal; |
| import com.sun.enterprise.ee.cms.core.AliveAndReadyView; |
| import com.sun.enterprise.ee.cms.core.CallBack; |
| import com.sun.enterprise.ee.cms.core.FailureNotificationActionFactory; |
| import com.sun.enterprise.ee.cms.core.FailureNotificationSignal; |
| import com.sun.enterprise.ee.cms.core.FailureRecoverySignal; |
| import com.sun.enterprise.ee.cms.core.FailureSuspectedActionFactory; |
| import com.sun.enterprise.ee.cms.core.GMSConstants; |
| import com.sun.enterprise.ee.cms.core.GMSException; |
| import com.sun.enterprise.ee.cms.core.GMSFactory; |
| import com.sun.enterprise.ee.cms.core.GroupLeadershipNotificationActionFactory; |
| import com.sun.enterprise.ee.cms.core.GroupManagementService; |
| import com.sun.enterprise.ee.cms.core.JoinNotificationActionFactory; |
| import com.sun.enterprise.ee.cms.core.JoinedAndReadyNotificationActionFactory; |
| import com.sun.enterprise.ee.cms.core.JoinedAndReadyNotificationSignal; |
| import com.sun.enterprise.ee.cms.core.PlannedShutdownActionFactory; |
| import com.sun.enterprise.ee.cms.core.PlannedShutdownSignal; |
| import com.sun.enterprise.ee.cms.core.ServiceProviderConfigurationKeys; |
| import com.sun.enterprise.ee.cms.core.Signal; |
| import com.sun.enterprise.ee.cms.impl.client.FailureNotificationActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.FailureRecoveryActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.FailureSuspectedActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.GroupLeadershipNotificationActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.JoinNotificationActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.JoinedAndReadyNotificationActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.MessageActionFactoryImpl; |
| import com.sun.enterprise.ee.cms.impl.client.PlannedShutdownActionFactoryImpl; |
| import com.sun.enterprise.mgmt.transport.NetworkUtility; |
| import com.sun.enterprise.mgmt.transport.grizzly.GrizzlyConfigConstants; |
| import com.sun.enterprise.util.io.ServerDirs; |
| import com.sun.logging.LogDomains; |
| |
| /** |
| * @author Sheetal.Vartak@Sun.COM |
| */ |
| @PerLookup |
| @Service() |
| public class GMSAdapterImpl implements GMSAdapter, PostConstruct, CallBack { |
| |
| //private static final Logger logger = |
| // LogDomains.getLogger(GMSAdapterImpl.class, LogDomains.GMS_LOGGER); |
| |
| private static final String BEGINS_WITH = "^"; |
| private static final String GMS_PROPERTY_PREFIX = "GMS_"; |
| private static final String GMS_PROPERTY_PREFIX_REGEXP = BEGINS_WITH + GMS_PROPERTY_PREFIX; |
| |
| private GroupManagementService gms; |
| |
| private final static String CORE = "CORE"; |
| private final static String SPECTATOR = "SPECTATOR"; |
| private final static String MEMBERTYPE_STRING = "MEMBER_TYPE"; |
| |
| // all set in postConstruct |
| private String instanceName = null; |
| private boolean isDas = false; |
| private Cluster cluster = null; |
| private String clusterName = null; |
| private Config clusterConfig = null; |
| //private long joinTime = 0L; |
| |
| private ConcurrentHashMap<CallBack, JoinNotificationActionFactory> callbackJoinActionFactoryMapping = |
| new ConcurrentHashMap<CallBack, JoinNotificationActionFactory>(); |
| private ConcurrentHashMap<CallBack, JoinedAndReadyNotificationActionFactory> callbackJoinedAndReadyActionFactoryMapping = |
| new ConcurrentHashMap<CallBack, JoinedAndReadyNotificationActionFactory>(); |
| private ConcurrentHashMap<CallBack, FailureNotificationActionFactory> callbackFailureActionFactoryMapping = |
| new ConcurrentHashMap<CallBack, FailureNotificationActionFactory>(); |
| private ConcurrentHashMap<CallBack, FailureSuspectedActionFactory> callbackFailureSuspectedActionFactoryMapping = |
| new ConcurrentHashMap<CallBack, FailureSuspectedActionFactory>(); |
| private ConcurrentHashMap<CallBack, PlannedShutdownActionFactory> callbackPlannedShutdownActionFactoryMapping = |
| new ConcurrentHashMap<CallBack, PlannedShutdownActionFactory>(); |
| private EventListener glassfishEventListener = null; |
| private boolean aliveAndReadyLoggingEnabled = false; |
| private boolean testFailureRecoveryHandler = false; |
| |
| @Inject |
| Events events; |
| |
| @Inject |
| ServerEnvironment env; |
| |
| @Inject @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME) |
| Server server; |
| |
| @Inject |
| ServiceLocator habitat; |
| |
| @Inject |
| Clusters clusters; |
| |
| @Inject |
| Nodes nodes; |
| |
| @Inject |
| Servers servers; |
| |
| private HealthHistory hHistory; |
| |
| @LoggerInfo(subsystem = "CLSTR", description="Group Management Service Adapter Logger", publish=true) |
| private static final String GMS_LOGGER_NAME = "jakarta.enterprise.cluster.gms"; |
| |
| |
| @LogMessagesResourceBundle |
| private static final String LOG_MESSAGES_RB = "org.glassfish.cluster.gms.LogMessages"; |
| |
| static final Logger GMS_LOGGER = Logger.getLogger(GMS_LOGGER_NAME, LOG_MESSAGES_RB); |
| |
| //gmsservice.no.cluster.name=GMSAD1001: no clustername to lookup |
| //GMSAD1001.diag.cause.1=Required information was not passed into method. |
| //GMSAD1001.diag.check.1=File issue with all relevant information. |
| @LogMessageInfo(message = "no clustername to lookup", |
| level="SEVERE", |
| cause="Required information was not passed into method.", |
| action="File issue with all relevant information.") |
| private static final String GMS_NO_CLUSTER_NAME="NCLS-CLSTR-10101"; |
| |
| //gmsservice.multiple.adapter=GMSAD1002: Multiple gms-adapter service for cluster {0} |
| //GMSAD1002.diag.cause.1=GMs module is being initialized more than once for the same cluster. |
| //GMSAD1002.diag.check.1=File issue with all relevant information. |
| @LogMessageInfo(message = "Multiple gms-adapter service for cluster {0}", |
| level="SEVERE", |
| cause="GMs module is being initialized more than once for the same cluster.", |
| action="File issue with all relevant information.") |
| private static final String GMS_MULTIPLE_ADAPTER="NCLS-CLSTR-10102"; |
| |
| //gmsservice.nocluster.warning=GMSAD1003: GMS cannot initialize with unknown cluster |
| //GMSAD1003.diag.cause.1=No cluster was found with this name in the domain configuration. |
| //GMSAD1003.diag.check.1=Check that domain exists in domain.xml. |
| @LogMessageInfo(message = "GMS cannot initialize with unknown cluster", |
| level="WARNING", |
| cause="No cluster was found with this name in the domain configuration.", |
| action="Check that domain exists in domain.xml.") |
| private static final String GMS_NO_CLUSTER_WARNING="NCLS-CLSTR-10103"; |
| |
| //gmsservice.started=GMSAD1004: Started GMS for instance {0} in group {1} |
| @LogMessageInfo(message = "Started GMS for instance {0} in group {1}", level="INFO") |
| private static final String GMS_STARTED="NCLS-CLSTR-10104"; |
| |
| |
| //gmsservice.member.joined.group=GMSAD1005: Member {0} joined group {1} |
| @LogMessageInfo(message = "Member {0} joined group {1}", level="INFO") |
| private static final String GMS_JOINED="NCLS-CLSTR-10105"; |
| |
| |
| //gmsservice.alive.ready.signal=GMSAD1007: AliveAndReady for signal: {0} for member: {1} of group: {2} current:[{3}] previous:[{4}] |
| @LogMessageInfo(message = "AliveAndReady for signal: {0} for member: {1} of group: {2} current:[{3}] previous:[{4}]", level="INFO") |
| private static final String GMS_ALIVE_AND_READY="NCLS-CLSTR-10107"; |
| |
| //gmsservice.server_shutdown.received=GMSAD1008: GMSAdapter for member: {0} group: {1} received GlassfishEventType: {2} |
| @LogMessageInfo(message = "GMSAdapter for member: {0} group: {1} received GlassfishEventType: {2}", level="INFO") |
| private static final String GMS_SERVER_SHUTDOWN_RECEIVED="NCLS-CLSTR-10108"; |
| |
| |
| //gmsexception.new.health.history=GMSAD1009: An exception occurred while creating the HealthHistory object: {0} |
| //GMSAD1009.diag.cause.1=An unexpected exception occurred. |
| //GMSAD1009.diag.check.1=See server log for more details. |
| @LogMessageInfo(message = "An exception occurred while creating the HealthHistory object: {0}", |
| level="WARNING", |
| cause="An unexpected exception occurred.", |
| action="See server log for more details.") |
| private static final String GMS_EXCEPTION_NEW_HEALTH_HISTORY="NCLS-CLSTR-10109"; |
| |
| |
| //gmsexception.processing.config.props=GMSAD1010: An exception occurred while processing GMS configuration properties: {0} |
| //GMSAD1010.diag.cause.1=An unexpected exception occurred. |
| //GMSAD1010.diag.check.1=See server log for more details. |
| @LogMessageInfo(message = "An exception occurred while processing GMS configuration properties: {0}", |
| level="WARNING", |
| cause="An unexpected exception occurred.", |
| action="See server log for more details.") |
| private static final String GMS_EXCEPTION_PROCESSING_CONFIG="NCLS-CLSTR-10110"; |
| |
| |
| //gmsexception.ignoring.property=GMSAD1011: Ignoring group-management-service property {0} with value of {1} due to {2} |
| //# todo: can we remove this try/catch? |
| //GMSAD1011.diag.cause.1=An illegal argument was passed into the Shoal GMS implementation. |
| //GMSAD1011.diag.check.1=Check the server log file for more information from Shoal-GMS. |
| @LogMessageInfo(message = "Ignoring group-management-service property {0} with value of {1} due to {2}", |
| level="WARNING", |
| cause="An illegal argument was passed into the Shoal GMS implementation.", |
| action="Check the server log file for more information from Shoal-GMS.") |
| private static final String GMS_EXCEPTION_IGNORING_PROPERTY="NCLS-CLSTR-10111"; |
| |
| //gmsexception.cluster.property.error=GMSAD1012: Error processing cluster property:{0} value:{1} due to exception {2} |
| //# todo: can we remove this try/catch? |
| //GMSAD1012.diag.cause.1=An unexpected exception occurred. |
| //GMSAD1012.diag.check.1=Check the server log file for more information from Shoal-GMS. |
| @LogMessageInfo(message = "Error processing cluster property:{0} value:{1} due to exception {2}", |
| level="WARNING", |
| cause="An unexpected exception occurred.", |
| action="Check the server log file for more information from Shoal-GMS.") |
| private static final String GMS_EXCEPTION_CLUSTER_PROPERTY_ERROR="NCLS-CLSTR-10112"; |
| |
| //gmsexception.cannot.get.group.module=GMSAD1013: Exception in getting GMS module for group {0}: {1} |
| //GMSAD1013.diag.cause.1=There was a problem withing the GMS implementation. |
| //GMSAD1013.diag.check.1=Check the server log file for more information from Shoal-GMS. |
| @LogMessageInfo(message = "Exception in getting GMS module for group {0}: {1}", |
| level="SEVERE", |
| cause="An unexpected exception occurred.", |
| action="Check the server log file for more information from Shoal-GMS.") |
| private static final String GMS_EXCEPTION_CANNOT_GET_GROUP_MODULE="NCLS-CLSTR-10113"; |
| |
| //gmsexception.update.health.history=GMSAD1014: An exception occurred while updating the instance health history table: {0} |
| //GMSAD1014.diag.cause.1=An unexpected exception occurred. |
| //GMSAD1014.diag.check.1=Check the log for Shoal-GMS exceptions. |
| @LogMessageInfo(message = "An exception occurred while updating the instance health history table: {0}", |
| level="WARNING", |
| cause="An unexpected exception occurred.", |
| action="Check the log file for more information from Shoal-GMS.") |
| private static final String GMS_EXCEPTION_UPDATE_HEALTH_HISTORY="NCLS-CLSTR-10114"; |
| |
| //gmsservice.failurerecovery.start.notification=GMSAD1015: start failure recovery callback for component: {0} failed member: {1} |
| @LogMessageInfo(message = "start failure recovery callback for component: {0} failed member: {1}", level="INFO") |
| private static final String GMS_FAILURERECOVERY_START="NCLS-CLSTR-10115"; |
| |
| //gmsservice.failurerecovery.completed.notification=GMSAD016: complete failure recovery callback for component: {0} failed member: {1} |
| @LogMessageInfo(message = "complete failure recovery callback for component: {0} failed member: {1}", level="INFO") |
| private static final String GMS_FAILURE_RECOVERY_COMPLETED="NCLS-CLSTR-10116"; |
| |
| //gmsservice.failed.to.start=GMSAD1017: GMS failed to start. See stack trace for additional information. |
| @LogMessageInfo(message = "GMS failed to start. See stack trace for additional information.", |
| level="SEVERE", |
| cause="An unexpected exception occurred.", |
| action="Check the log file for more information") |
| private static final String GMS_FAILED_TO_START="NCLS-CLSTR-10117"; |
| |
| //gmsservice.failed.to.start.unexpected=GMSAD1018: GMS failed to start due to a runtime exception. See stack trace for additional information. |
| @LogMessageInfo(message = "GMS failed to start due to a runtime exception. See stack trace for additional information.", |
| level="SEVERE", |
| cause="An unexpected exception occurred.", |
| action="Check the log file for more information" |
| ) |
| private static final String GMS_FAILED_TO_START_UNEXCEPTED="NCLS-CLSTR-10118"; |
| |
| //gmsservice.bind.int.address.invalid=GMSAD1019: GMS bind interface address {0} is invalid. Will use default value instead. |
| //GMSAD1019.diag.cause.1=The specified bind interface address is not an active local address, so it cannot be used on this node. |
| //GMSAD1019.diag.check.1=Check that you have specified the proper address. See server log for more details from GMS subsystem. |
| @LogMessageInfo(message = "GMS bind interface address {0} is invalid. Will use default value instead.", |
| level="SEVERE", |
| cause="The specified bind interface address is not an active local address, so it cannot be used on this node.", |
| action="Check that you have specified the proper address. See server log for more details from GMS subsystem.") |
| private static final String GMS_BIND_INT_ADDRESS_INVALID="NCLS-CLSTR-10119"; |
| |
| //gmsservice.listener.port.required=GMSAD1020: GMS listener port is required for cluster {0}. Will attempt to use default of {1}. |
| @LogMessageInfo(message = "GMS listener port is required for cluster {0}. Will attempt to use default of {1}.", level="WARNING") |
| private static final String GMS_LISTENER_PORT_REQUIRED="NCLS-CLSTR-10120"; |
| |
| @Override |
| public void postConstruct() { |
| } |
| |
| AtomicBoolean initialized = new AtomicBoolean(false); |
| AtomicBoolean initializationComplete = new AtomicBoolean(false); |
| |
| @Override |
| public String getClusterName() { |
| return clusterName; |
| } |
| |
| @Override |
| public boolean initialize(String clusterName) { |
| if (initialized.compareAndSet(false, true)) { |
| this.clusterName = clusterName; |
| if (clusterName == null) { |
| GMS_LOGGER.log(LogLevel.SEVERE, GMS_NO_CLUSTER_NAME); |
| return false; |
| } |
| try { |
| gms = GMSFactory.getGMSModule(clusterName); |
| } catch (GMSException ge) { |
| // ignore |
| } |
| if (gms != null) { |
| GMS_LOGGER.log(LogLevel.SEVERE, GMS_MULTIPLE_ADAPTER, |
| clusterName); |
| return false; |
| } |
| |
| Domain domain = habitat.getService(Domain.class); |
| instanceName = env.getInstanceName(); |
| isDas = env.isDas(); |
| cluster = server.getCluster(); |
| if (cluster == null && clusters != null) { |
| // must be the DAS since it not direclty considered a member of cluster by domain.xml. |
| // iterate over all clusters to find the cluster that has name passed in. |
| for (Cluster clusterI : clusters.getCluster()) { |
| if (clusterName.compareTo(clusterI.getName()) == 0) { |
| cluster = clusterI; |
| break; |
| } |
| } |
| } |
| if (cluster == null) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_NO_CLUSTER_WARNING); |
| return false; //don't enable GMS |
| } else if (isDas) { |
| // only want to do this in the case of the DAS |
| initializeHealthHistory(cluster); |
| } |
| |
| clusterConfig = domain.getConfigNamed(clusterName + "-config"); |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "clusterName=" + clusterName + |
| " clusterConfig=" + clusterConfig); |
| } |
| try { |
| initializeGMS(); |
| } catch (GMSException e) { |
| GMS_LOGGER.log(LogLevel.SEVERE, GMS_FAILED_TO_START, e); |
| // prevent access to a malformed gms object. |
| return false; |
| |
| // also ensure for any unchecked exceptions (such as NPE during initialization) during initialization |
| // that the malformed gms object is not allowed to be accesssed through the gms adapter. |
| } catch (Throwable t) { |
| GMS_LOGGER.log(LogLevel.SEVERE, GMS_FAILED_TO_START_UNEXCEPTED, t); |
| // prevent access to a malformed gms object. |
| return false; |
| } |
| initializationComplete.set(true); |
| } |
| return initialized.get(); |
| } |
| |
| @Override |
| public void complete() { |
| initialized.compareAndSet(true, false); |
| initializationComplete.compareAndSet(true, false); |
| gms = null; |
| GMSFactory.removeGMSModule(clusterName); |
| } |
| |
| @Override |
| public HealthHistory getHealthHistory() { |
| checkInitialized(); |
| return hHistory; |
| } |
| |
| private void initializeHealthHistory(Cluster cluster) { |
| try { |
| /* |
| * Should not fail, but we need to make sure it doesn't |
| * affect GMS just in case. |
| */ |
| hHistory = new HealthHistory(cluster); |
| Dom.unwrap(cluster).addListener(hHistory); |
| } catch (Throwable t) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_EXCEPTION_NEW_HEALTH_HISTORY, |
| t.getLocalizedMessage()); |
| } |
| } |
| |
| private void readGMSConfigProps(Properties configProps) { |
| configProps.put(MEMBERTYPE_STRING, isDas ? SPECTATOR : CORE); |
| for (ServiceProviderConfigurationKeys key : ServiceProviderConfigurationKeys.values()) { |
| String keyName = key.toString(); |
| try { |
| switch (key) { |
| case MULTICASTADDRESS: |
| if (cluster != null) { |
| String value = cluster.getGmsMulticastAddress(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case MULTICASTPORT: |
| if (cluster != null) { |
| String value = cluster.getGmsMulticastPort(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case FAILURE_DETECTION_TIMEOUT: |
| if (clusterConfig != null) { |
| String value = clusterConfig.getGroupManagementService().getFailureDetection().getHeartbeatFrequencyInMillis(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case FAILURE_DETECTION_RETRIES: |
| if (clusterConfig != null) { |
| String value = clusterConfig.getGroupManagementService().getFailureDetection().getMaxMissedHeartbeats(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case FAILURE_VERIFICATION_TIMEOUT: |
| if (clusterConfig != null) { |
| String value = clusterConfig.getGroupManagementService().getFailureDetection().getVerifyFailureWaittimeInMillis(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case DISCOVERY_TIMEOUT: |
| if (clusterConfig != null) { |
| String value = clusterConfig.getGroupManagementService().getGroupDiscoveryTimeoutInMillis(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case IS_BOOTSTRAPPING_NODE: |
| configProps.put(keyName, isDas ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); |
| break; |
| |
| |
| case BIND_INTERFACE_ADDRESS: |
| if (cluster != null) { |
| String value = cluster.getGmsBindInterfaceAddress(); |
| if (value != null) { |
| value = value.trim(); |
| } |
| if (value != null && value.length() > 1 && value.charAt(0) != '$') { |
| |
| // todo: remove check for value length greater than 1. |
| // this value could be anything from IPv4 address, IPv6 address, hostname, network interface name. |
| // Only supported IPv4 address in gf v2. |
| if (NetworkUtility.isBindAddressValid(value)) { |
| configProps.put(keyName, value); |
| } else { |
| GMS_LOGGER.log(LogLevel.SEVERE, |
| GMS_BIND_INT_ADDRESS_INVALID, |
| value); |
| } |
| } |
| } |
| break; |
| |
| case FAILURE_DETECTION_TCP_RETRANSMIT_TIMEOUT: |
| if (clusterConfig != null) { |
| String value = clusterConfig.getGroupManagementService().getFailureDetection().getVerifyFailureConnectTimeoutInMillis(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| } |
| break; |
| |
| case MULTICAST_POOLSIZE: |
| case INCOMING_MESSAGE_QUEUE_SIZE : |
| // case MAX_MESSAGE_LENGTH: todo uncomment with shoal-gms.jar with this defined is promoted. |
| case FAILURE_DETECTION_TCP_RETRANSMIT_PORT: |
| |
| if (clusterConfig != null) { |
| Property prop = clusterConfig.getGroupManagementService().getProperty(keyName); |
| if (prop == null) { |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "No config property found for %s", |
| keyName)); |
| } |
| break; |
| } |
| String value = prop.getValue().trim(); |
| if (value != null) { |
| configProps.put(keyName, value); |
| } |
| /* |
| int positiveint = 0; |
| try { |
| positiveint = Integer.getInteger(value); |
| } catch (Throwable t) {} |
| |
| // todo |
| if (positiveint > 0) { |
| configProps.put(keyName, positiveint); |
| } // todo else log event that invalid value was provided. |
| */ |
| } |
| break; |
| |
| // These Shoal GMS configuration parameters are not supported to be set. |
| // Must place here or they will get flagged as not handled. |
| case LOOPBACK: |
| case VIRTUAL_MULTICAST_URI_LIST: |
| break; |
| |
| // end unsupported Shoal GMS configuration parameters. |
| |
| |
| default: |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "service provider key %s ignored", keyName)); |
| } |
| break; |
| } /* end switch over ServiceProviderConfigurationKeys enum */ |
| } catch (Throwable t) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_EXCEPTION_PROCESSING_CONFIG, t.getLocalizedMessage()); |
| } |
| } /* end for loop over ServiceProviderConfigurationKeys */ |
| |
| // check for Grizzly transport specific properties in GroupManagementService property list and then cluster property list. |
| // cluster property is more specific than group-mangement-service, so allow cluster property to override group-management-service proeprty |
| // if a GrizzlyConfigConstant property is in both list. |
| List<Property> props = null; |
| if (clusterConfig != null) { |
| props = clusterConfig.getGroupManagementService().getProperty(); |
| for (Property prop : props) { |
| String name = prop.getName().trim(); |
| String value = prop.getValue().trim(); |
| if (name == null || value == null) { |
| continue; |
| } |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "processing group-management-service property name=" + |
| name + " value= " + value); |
| } |
| if (value.startsWith("${")) { |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "skipping group-management-service property name=" + |
| name + |
| " since value is unresolved symbolic token=" + |
| value); |
| } |
| } else { |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "processing group-management-service property name=" + |
| name + " value= " + value); |
| } |
| if (name.startsWith(GMS_PROPERTY_PREFIX)) { |
| name = name.replaceFirst(GMS_PROPERTY_PREFIX_REGEXP, ""); |
| } |
| configProps.put(name, value); |
| if (! validateGMSProperty(name)) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_EXCEPTION_IGNORING_PROPERTY, |
| new Object [] {name, value, ""} ); |
| } |
| } |
| } |
| } |
| if (cluster != null) { |
| props = cluster.getProperty(); |
| for (Property prop : props) { |
| String name = prop.getName().trim(); |
| String value = prop.getValue().trim(); |
| if (name == null || value == null) { |
| continue; |
| } |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "processing cluster property name=" + name + |
| " value= " + value); |
| } |
| if (value.startsWith("${")) { |
| if (GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "skipping cluster property name=" + name + |
| " since value is unresolved symbolic token=" + |
| value); |
| } |
| } else { |
| if (name.startsWith(GMS_PROPERTY_PREFIX)) { |
| name = name.replaceFirst(GMS_PROPERTY_PREFIX_REGEXP, ""); |
| } |
| // undocumented property for testing purposes. |
| // impossible to register handlers in a regular app before gms starts up. |
| if (name.compareTo("ALIVEANDREADY_LOGGING") == 0){ |
| aliveAndReadyLoggingEnabled = Boolean.parseBoolean(value); |
| } else if (name.compareTo("LISTENER_PORT") == 0 ) { |
| |
| // special case mapping. Glassfish Cluster property GMS_LISTENER_PORT maps to Grizzly Config Constants TCPSTARTPORT and TCPENDPORT. |
| configProps.put(GrizzlyConfigConstants.TCPSTARTPORT.toString(), value); |
| configProps.put(GrizzlyConfigConstants.TCPENDPORT.toString(), value); |
| } else if (name.compareTo("TEST_FAILURE_RECOVERY") == 0) { |
| testFailureRecoveryHandler = Boolean.parseBoolean(value); |
| } else if (ServiceProviderConfigurationKeys |
| .DISCOVERY_URI_LIST.name().equals(name) && |
| "generate".equals(value)) { |
| |
| value = generateDiscoveryUriList(); |
| configProps.put(name, value); |
| } else { |
| // handle normal case. one to one mapping. |
| configProps.put(name, value); |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "processing cluster property name=" + name + |
| " value= " + value); |
| if (! validateGMSProperty(name)) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_EXCEPTION_CLUSTER_PROPERTY_ERROR, |
| new Object [] {name, value, ""} ); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Get existing nodes based on cluster element in domain. |
| * Then check for DAS address in das.properties. When the |
| * list is set to 'generate' then the gms listener port |
| * must also be specified. So the same port is used for |
| * each cluster member. |
| */ |
| private String generateDiscoveryUriList() { |
| String clusterPort = null; |
| |
| Property gmsPortProp = cluster.getProperty("GMS_LISTENER_PORT"); |
| if (gmsPortProp == null || |
| gmsPortProp.getValue() == null || |
| gmsPortProp.getValue().trim().charAt(0) == '$') { |
| |
| clusterPort = "9090"; |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_LISTENER_PORT_REQUIRED, |
| new Object [] {cluster.getName(), clusterPort}); |
| } else { |
| clusterPort = gmsPortProp.getValue(); |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, "will use gms listener port: " + |
| clusterPort); |
| } |
| } |
| |
| // get cluster member server refs |
| Set<String> instanceNames = new HashSet<String>(); |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "checking cluster.getServerRef() for '%s'", |
| cluster.getName())); |
| } |
| for (ServerRef sRef : cluster.getServerRef()) { |
| |
| /* |
| * When an instance (not DAS) starts up, it will add |
| * its own address to the discovery list. This is ok |
| * now. If we want to skip it, here's the place to |
| * check. |
| */ |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "adding server ref %s to set of instance names", |
| sRef.getRef())); |
| } |
| instanceNames.add(sRef.getRef()); |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| final String SEP = ","; |
| final String scheme = "tcp://"; |
| |
| // use server refs to find matching nodes |
| for (String name : instanceNames) { |
| Server server = servers.getServer(name); |
| if (server != null) { |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "found server for name %s", |
| name)); |
| } |
| Node node = nodes.getNode(server.getNodeRef()); |
| if (node != null) { |
| String host = scheme + node.getNodeHost() + ":" + |
| clusterPort; |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "Adding host '%s' to discovery list", host)); |
| } |
| sb.append(host).append(SEP); |
| } |
| } |
| } |
| |
| // add das location from das.properties if needed |
| if (server.isInstance()) { |
| try { |
| ServerDirs sDirs = new ServerDirs(env.getInstanceRoot()); |
| File dasPropsFile = sDirs.getDasPropertiesFile(); |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "found das.props file at %s", |
| dasPropsFile.getAbsolutePath())); |
| } |
| Properties dasProps = getProperties(dasPropsFile); |
| String host = scheme + |
| dasProps.getProperty("agent.das.host") + |
| ":" + |
| clusterPort; |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "adding '%s' from das.props file", host)); |
| } |
| sb.append(host).append(SEP); |
| } catch (IOException ioe) { |
| GMS_LOGGER.log(LogLevel.WARNING, ioe.toString()); |
| } |
| } |
| |
| // trim list if needed and return |
| int lastCommaIndex = sb.lastIndexOf(SEP); |
| if (lastCommaIndex != -1) { |
| sb.deleteCharAt(lastCommaIndex); |
| } |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, String.format( |
| "returning discovery list '%s'", |
| sb.toString())); |
| } |
| return sb.toString(); |
| } |
| |
| final protected Properties getProperties(File propFile) throws IOException { |
| Properties props = new Properties(); |
| FileInputStream fis = null; |
| try { |
| fis = new FileInputStream(propFile); |
| props.load(fis); |
| fis.close(); |
| fis = null; |
| } finally { |
| if (fis != null) { |
| try { |
| fis.close(); |
| } catch (IOException ignored) {} |
| } |
| } |
| return props; |
| } |
| |
| private boolean validateGMSProperty(String propertyName) { |
| boolean result = false; |
| Object key = null; |
| try { |
| key = GrizzlyConfigConstants.valueOf(propertyName); |
| result = true; |
| } catch (Throwable ignored) {} |
| if (key == null) { |
| try { |
| key = ServiceProviderConfigurationKeys.valueOf(propertyName); |
| result = true; |
| } catch (Throwable ignored) {} |
| } |
| return key != null && result; |
| } |
| |
| private void initializeGMS() throws GMSException{ |
| Properties configProps = new Properties(); |
| int HA_MAX_GMS_MESSAGE_LENGTH = 4 * (1024 * 1024) + (2 * 1024); // Default to 4 MB limit in glassfish. |
| configProps.put(ServiceProviderConfigurationKeys.MAX_MESSAGE_LENGTH.toString(), Integer.toString(HA_MAX_GMS_MESSAGE_LENGTH)); |
| |
| |
| // read GMS configuration from domain.xml |
| readGMSConfigProps(configProps); |
| |
| printProps(configProps); |
| |
| String memberType = (String) configProps.get(MEMBERTYPE_STRING); |
| gms = (GroupManagementService) GMSFactory.startGMSModule(instanceName, clusterName, |
| GroupManagementService.MemberType.valueOf(memberType), configProps); |
| //remove GMSLogDomain.getLogger(GMSLogDomain.GMS_LOGGER).setLevel(gmsLogLevel); |
| GMSFactory.setGMSEnabledState(clusterName, Boolean.TRUE); |
| if (gms != null) { |
| try { |
| registerJoinedAndReadyNotificationListener(this); |
| registerJoinNotificationListener(this); |
| registerFailureNotificationListener(this); |
| registerPlannedShutdownListener(this); |
| registerFailureSuspectedListener(this); |
| |
| //fix gf it 12905 |
| if (testFailureRecoveryHandler && ! env.isDas()) { |
| |
| // this must be here or appointed recovery server notification is not printed out for automated testing. |
| registerFailureRecoveryListener("GlassfishFailureRecoveryHandlerTest", this); |
| } |
| |
| glassfishEventListener = new org.glassfish.api.event.EventListener() { |
| public void event(Event event) { |
| if (gms == null) { |
| // handle cases where gms is not set and for some reason this handler did not get unregistered. |
| return; |
| } |
| if (event.is(EventTypes.SERVER_SHUTDOWN)) { |
| GMS_LOGGER.log(LogLevel.INFO, GMS_SERVER_SHUTDOWN_RECEIVED, |
| new Object[]{gms.getInstanceName(), gms.getGroupName(), event.name()}); |
| |
| // todo: remove these when removing the test register ones above. |
| removeJoinedAndReadyNotificationListener(GMSAdapterImpl.this); |
| removeJoinNotificationListener(GMSAdapterImpl.this); |
| removeFailureNotificationListener(GMSAdapterImpl.this); |
| removeFailureSuspectedListener(GMSAdapterImpl.this); |
| gms.shutdown(GMSConstants.shutdownType.INSTANCE_SHUTDOWN); |
| removePlannedShutdownListener(GMSAdapterImpl.this); |
| events.unregister(glassfishEventListener); |
| } else if (event.is(EventTypes.SERVER_READY)) { |
| // consider putting following, includding call to joinedAndReady into a timertask. |
| // this time would give instance time to get its heartbeat cache updated by all running |
| // READY cluster memebrs |
| // final long MAX_WAIT_DURATION = 4000; |
| // |
| // long elapsedDuration = (joinTime == 0L) ? 0 : System.currentTimeMillis() - joinTime; |
| // long waittime = MAX_WAIT_DURATION - elapsedDuration; |
| // if (waittime > 0L && waittime <= MAX_WAIT_DURATION) { |
| // try { |
| // GMS_LOGGER.info("wait " + waittime + " ms before signaling joined and ready"); |
| // Thread.sleep(waittime); |
| // } catch(Throwable t) {} |
| // } |
| // validateCoreMembers(); |
| gms.reportJoinedAndReadyState(); |
| } |
| } |
| }; |
| events.register(glassfishEventListener); |
| gms.join(); |
| //joinTime = System.currentTimeMillis(); |
| GMS_LOGGER.log(LogLevel.INFO, GMS_JOINED, |
| new Object [] {instanceName, clusterName}); |
| } catch (GMSException e) { |
| // failed to start so unregister event listener that calls GMS. |
| events.unregister(glassfishEventListener); |
| throw e; |
| } |
| |
| GMS_LOGGER.log(LogLevel.INFO, GMS_STARTED, |
| new Object[] {instanceName, clusterName}); |
| |
| } else throw new GMSException("gms object is null."); |
| } |
| |
| private void printProps(Properties prop) { |
| if (!GMS_LOGGER.isLoggable(LogLevel.CONFIG)) { |
| return; |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| for (String key : prop.stringPropertyNames()) { |
| sb.append(key).append(" = ").append(prop.get(key)).append(" "); |
| } |
| GMS_LOGGER.log(LogLevel.CONFIG, |
| "Printing all GMS properties: ", sb.toString()); |
| } |
| |
| private void checkInitialized() { |
| if( ! initialized.get() || ! initializationComplete.get()) { |
| throw new IllegalStateException("GMSAdapter not properly initialized."); |
| } |
| } |
| @Override |
| public GroupManagementService getModule() { |
| checkInitialized(); |
| return gms; |
| } |
| |
| public GroupManagementService getGMS(String groupName) { |
| //return the gms instance for that group |
| try { |
| return GMSFactory.getGMSModule(groupName); |
| } catch (GMSException e) { |
| GMS_LOGGER.log(LogLevel.SEVERE, GMS_EXCEPTION_CANNOT_GET_GROUP_MODULE, |
| new Object [] {groupName , e.getLocalizedMessage()}); |
| return null; |
| } |
| } |
| |
| @Override |
| public void processNotification(Signal signal) { |
| if (GMS_LOGGER.isLoggable(LogLevel.FINE)) { |
| GMS_LOGGER.log(LogLevel.FINE, "GMSService: Received a notification ", |
| signal.getClass().getName()); |
| } |
| try { |
| /* |
| * Should not fail, but we need to make sure it doesn't |
| * affect GMS just in case. In the non-DAS case, hHistory |
| * will always be null so we skip it. In the DAS case, |
| * it shouldn't be null unless we've already seen an |
| * error logged during construction. |
| */ |
| if (hHistory != null) { |
| hHistory.updateHealth(signal); |
| } |
| } catch (Throwable t) { |
| GMS_LOGGER.log(LogLevel.WARNING, GMS_EXCEPTION_UPDATE_HEALTH_HISTORY, |
| t.getLocalizedMessage()); |
| } |
| // testing only. one must set cluster property GMS_TEST_FAILURE_RECOVERY to true for the following to execute. */ |
| if (testFailureRecoveryHandler && signal instanceof FailureRecoverySignal) { |
| FailureRecoverySignal frsSignal = (FailureRecoverySignal)signal; |
| GMS_LOGGER.log(LogLevel.INFO, GMS_FAILURERECOVERY_START, new Object[]{frsSignal.getComponentName(), frsSignal.getMemberToken()}); |
| try { |
| Thread.sleep(20 * 1000); // sleep 20 seconds. simulate wait time to allow instance to restart and do self recovery before another instance does it. |
| } catch (InterruptedException ignored) { |
| } |
| GMS_LOGGER.log(LogLevel.INFO, GMS_FAILURE_RECOVERY_COMPLETED, new Object[]{frsSignal.getComponentName(), frsSignal.getMemberToken()}); |
| } |
| if (this.aliveAndReadyLoggingEnabled) { |
| if (signal instanceof JoinedAndReadyNotificationSignal || |
| signal instanceof FailureNotificationSignal || |
| signal instanceof PlannedShutdownSignal) { |
| AliveAndReadySignal arSignal = (AliveAndReadySignal)signal; |
| String signalSubevent = ""; |
| if (signal instanceof JoinedAndReadyNotificationSignal) { |
| JoinedAndReadyNotificationSignal jrsig = (JoinedAndReadyNotificationSignal)signal; |
| if (jrsig.getEventSubType() == GMSConstants.startupType.GROUP_STARTUP) { |
| signalSubevent = " Subevent: " + GMSConstants.startupType.GROUP_STARTUP; |
| } else if (jrsig.getRejoinSubevent() != null) { |
| signalSubevent = " Subevent: " + jrsig.getRejoinSubevent(); |
| } |
| } |
| if (signal instanceof PlannedShutdownSignal) { |
| PlannedShutdownSignal pssig = (PlannedShutdownSignal)signal; |
| if (pssig.getEventSubType() == GMSConstants.shutdownType.GROUP_SHUTDOWN) { |
| signalSubevent = " Subevent:" + GMSConstants.shutdownType.GROUP_SHUTDOWN.toString(); |
| } |
| } |
| AliveAndReadyView current = arSignal.getCurrentView(); |
| AliveAndReadyView previous = arSignal.getPreviousView(); |
| GMS_LOGGER.log(LogLevel.INFO, GMS_ALIVE_AND_READY, |
| new Object [] { |
| signal.getClass().getSimpleName() + signalSubevent, |
| signal.getMemberToken(), |
| signal.getGroupName(), |
| current, |
| previous |
| }); |
| } |
| } |
| } |
| |
| // each of the getModule(s) methods are temporary. see class-level comment. |
| |
| /** |
| * Registers a JoinNotification Listener. |
| * |
| * @param callback processes GMS notification JoinNotificationSignal |
| */ |
| @Override |
| public void registerJoinNotificationListener(CallBack callback) { |
| if (gms != null && callback != null) { |
| JoinNotificationActionFactory jnaf = new JoinNotificationActionFactoryImpl(callback); |
| gms.addActionFactory(jnaf); |
| callbackJoinActionFactoryMapping.put(callback, jnaf); |
| } |
| } |
| |
| /** |
| * Registers a JoinAndReadyNotification Listener. |
| * |
| * @param callback processes GMS notification JoinAndReadyNotificationSignal |
| */ |
| @Override |
| public void registerJoinedAndReadyNotificationListener(CallBack callback) { |
| if (gms != null && callback != null) { |
| JoinedAndReadyNotificationActionFactory jnaf = new JoinedAndReadyNotificationActionFactoryImpl(callback); |
| gms.addActionFactory(jnaf); |
| callbackJoinedAndReadyActionFactoryMapping.put(callback, jnaf); |
| } |
| } |
| |
| /** |
| * Register a listener for all events that represent a member has left the group. |
| * |
| * @param callback Signal can be either PlannedShutdownSignal, FailureNotificationSignal or JoinNotificationSignal(subevent Rejoin). |
| */ |
| @Override |
| public void registerMemberLeavingListener(CallBack callback) { |
| if (gms != null && callback != null) { |
| registerFailureNotificationListener(callback); |
| registerPlannedShutdownListener(callback); |
| registerJoinNotificationListener(callback); |
| } |
| } |
| |
| /** |
| * Registers a PlannedShutdown Listener. |
| * |
| * @param callback processes GMS notification PlannedShutdownSignal |
| */ |
| @Override |
| public void registerPlannedShutdownListener(CallBack callback) { |
| if (gms != null && callback != null) { |
| PlannedShutdownActionFactory psaf = new PlannedShutdownActionFactoryImpl(callback); |
| callbackPlannedShutdownActionFactoryMapping.put(callback, psaf); |
| gms.addActionFactory(psaf); |
| } |
| } |
| |
| /** |
| * Registers a FailureSuspected Listener. |
| * |
| * @param callback processes GMS notification FailureSuspectedSignal |
| */ |
| @Override |
| public void registerFailureSuspectedListener(CallBack callback) { |
| if (gms != null) { |
| FailureSuspectedActionFactory fsaf = new FailureSuspectedActionFactoryImpl(callback); |
| callbackFailureSuspectedActionFactoryMapping.put(callback, fsaf); |
| gms.addActionFactory(fsaf); |
| } |
| } |
| |
| /** |
| * Registers a FailureNotification Listener. |
| * |
| * @param callback processes GMS notification FailureNotificationSignal |
| */ |
| @Override |
| public void registerFailureNotificationListener(CallBack callback) { |
| if (gms != null) { |
| FailureNotificationActionFactory fnaf = new FailureNotificationActionFactoryImpl(callback); |
| callbackFailureActionFactoryMapping.put(callback, fnaf); |
| gms.addActionFactory(fnaf); |
| } |
| } |
| |
| /** |
| * Registers a FailureRecovery Listener. |
| * |
| * @param callback processes GMS notification FailureRecoverySignal |
| * @param componentName The name of the parent application's component that should be notified of selected for |
| * performing recovery operations. One or more components in the parent application may |
| * want to be notified of such selection for their respective recovery operations. |
| */ |
| @Override |
| public void registerFailureRecoveryListener(String componentName, CallBack callback) { |
| if (gms != null) { |
| gms.addActionFactory(componentName, new FailureRecoveryActionFactoryImpl(callback)); |
| } |
| } |
| |
| /** |
| * Registers a Message Listener. |
| * |
| * @param componentName Name of the component that would like to consume |
| * Messages. One or more components in the parent application would want to |
| * be notified when messages arrive addressed to them. This registration |
| * allows GMS to deliver messages to specific components. |
| * @param messageListener processes GMS MessageSignal |
| */ |
| @Override |
| public void registerMessageListener(String componentName, CallBack messageListener) { |
| if (gms != null) { |
| gms.addActionFactory(new MessageActionFactoryImpl(messageListener), componentName); |
| } |
| } |
| |
| /** |
| * Registers a GroupLeadershipNotification Listener. |
| * |
| * @param callback processes GMS notification GroupLeadershipNotificationSignal. This event occurs when the GMS masters leaves the Group |
| * and another member of the group takes over leadership. The signal indicates the new leader. |
| */ |
| @Override |
| public void registerGroupLeadershipNotificationListener(CallBack callback) { |
| if (gms != null) { |
| gms.addActionFactory(new GroupLeadershipNotificationActionFactoryImpl(callback)); |
| } |
| } |
| |
| @Override |
| public void removeFailureRecoveryListener(String componentName) { |
| if (gms != null) { |
| gms.removeFailureRecoveryActionFactory(componentName); |
| } |
| } |
| |
| @Override |
| public void removeMessageListener(String componentName){ |
| if (gms != null) { |
| gms.removeMessageActionFactory(componentName); |
| } |
| } |
| |
| @Override |
| public void removeFailureNotificationListener(CallBack callback){ |
| if (gms != null) { |
| FailureNotificationActionFactory fnaf = callbackFailureActionFactoryMapping.remove(callback); |
| if (fnaf != null) { |
| gms.removeActionFactory(fnaf); |
| } |
| } |
| } |
| |
| @Override |
| public void removeFailureSuspectedListener(CallBack callback){ |
| if (gms != null) { |
| FailureSuspectedActionFactory fsaf = callbackFailureSuspectedActionFactoryMapping.remove(callback); |
| if (fsaf != null) { |
| gms.removeFailureSuspectedActionFactory(fsaf); |
| } |
| } |
| } |
| |
| @Override |
| public void removeJoinNotificationListener(CallBack callback){ |
| if (gms != null) { |
| JoinNotificationActionFactory jaf = callbackJoinActionFactoryMapping.get(callback); |
| if (jaf != null) { |
| gms.removeActionFactory(jaf); |
| } |
| } |
| } |
| |
| @Override |
| public void removeJoinedAndReadyNotificationListener(CallBack callback){ |
| if (gms != null) { |
| JoinedAndReadyNotificationActionFactory jaf = callbackJoinedAndReadyActionFactoryMapping.get(callback); |
| if (jaf != null) { |
| gms.removeActionFactory(jaf); |
| } |
| } |
| } |
| |
| @Override |
| public void removePlannedShutdownListener(CallBack callback){ |
| if (gms != null) { |
| PlannedShutdownActionFactory psaf = callbackPlannedShutdownActionFactoryMapping.remove(callback); |
| if (psaf != null) { |
| gms.removeActionFactory(psaf); |
| } |
| } |
| } |
| |
| @Override |
| public void removeGroupLeadershipNotificationListener(CallBack callback){ |
| } |
| |
| @Override |
| public void removeMemberLeavingListener(CallBack callback){ |
| removePlannedShutdownListener(callback); |
| removeFailureNotificationListener(callback); |
| removeJoinNotificationListener(callback); |
| } |
| |
| } |