/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.clustering.tribes; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.axis2.AxisFault; import org.apache.axis2.clustering.*; import org.apache.axis2.clustering.control.ControlCommand; import org.apache.axis2.clustering.control.GetConfigurationCommand; import org.apache.axis2.clustering.control.GetStateCommand; import org.apache.axis2.clustering.management.DefaultGroupManagementAgent; import org.apache.axis2.clustering.management.DefaultNodeManager; import org.apache.axis2.clustering.management.GroupManagementAgent; import org.apache.axis2.clustering.management.NodeManager; import org.apache.axis2.clustering.state.ClusteringContextListener; import org.apache.axis2.clustering.state.DefaultStateManager; import org.apache.axis2.clustering.state.StateManager; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.util.JavaUtils; import org.apache.axis2.description.HandlerDescription; import org.apache.axis2.description.Parameter; import org.apache.axis2.description.PhaseRule; import org.apache.axis2.description.TransportInDescription; import org.apache.axis2.engine.AxisConfiguration; import org.apache.axis2.engine.DispatchPhase; import org.apache.axis2.engine.Phase; import org.apache.catalina.tribes.Channel; import org.apache.catalina.tribes.ChannelException; import org.apache.catalina.tribes.ErrorHandler; import org.apache.catalina.tribes.ManagedChannel; import org.apache.catalina.tribes.Member; import org.apache.catalina.tribes.UniqueId; import org.apache.catalina.tribes.group.GroupChannel; import org.apache.catalina.tribes.group.Response; import org.apache.catalina.tribes.group.RpcChannel; import org.apache.catalina.tribes.group.interceptors.NonBlockingCoordinator; import org.apache.catalina.tribes.transport.MultiPointSender; import org.apache.catalina.tribes.transport.ReplicationTransmitter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.xml.namespace.QName; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * The main ClusteringAgent class for the Tribes based clustering implementation */ public class TribesClusteringAgent implements ClusteringAgent { private static final Log log = LogFactory.getLog(TribesClusteringAgent.class); public static final String DEFAULT_SUB_DOMAIN = "__$default"; private DefaultNodeManager configurationManager; private DefaultStateManager contextManager; private final HashMap<String, Parameter> parameters; private ManagedChannel channel; /** * RpcChannel used for cluster initialization interactions */ private RpcChannel rpcInitChannel; /** * RpcChannel used for RPC messaging interactions */ private RpcChannel rpcMessagingChannel; private ConfigurationContext configurationContext; private Axis2ChannelListener axis2ChannelListener; private ChannelSender channelSender; private MembershipManager primaryMembershipManager; private RpcInitializationRequestHandler rpcInitRequestHandler; private MembershipScheme membershipScheme; private NonBlockingCoordinator coordinator; /** * The mode in which this member operates such as "loadBalance" or "application" */ private OperationMode mode; /** * Static members */ private List<org.apache.axis2.clustering.Member> members; /** * Map[key, value=Map[key, value]] = [domain, [subDomain, GroupManagementAgent]] */ private final Map<String, Map<String, GroupManagementAgent>> groupManagementAgents = new HashMap<String, Map<String, GroupManagementAgent>>(); private boolean clusterManagementMode; private RpcMessagingHandler rpcMessagingHandler; public TribesClusteringAgent() { parameters = new HashMap<String, Parameter>(); } public void setMembers(List<org.apache.axis2.clustering.Member> members) { this.members = members; } public List<org.apache.axis2.clustering.Member> getMembers() { return members; } public int getAliveMemberCount() { return primaryMembershipManager.getMembers().length; } public void addGroupManagementAgent(GroupManagementAgent agent, String applicationDomain) { addGroupManagementAgent(agent, applicationDomain, null); } public void addGroupManagementAgent(GroupManagementAgent agent, String applicationDomain, String applicationSubDomain) { if (applicationSubDomain == null) { applicationSubDomain = DEFAULT_SUB_DOMAIN; // default sub-domain since a sub-domain is not specified } log.info("Managing group application domain:" + applicationDomain + ", sub-domain:" + applicationSubDomain + " using agent " + agent.getClass()); if(!groupManagementAgents.containsKey(applicationDomain)){ groupManagementAgents.put(applicationDomain, new HashMap<String, GroupManagementAgent>()); } groupManagementAgents.get(applicationDomain).put(applicationSubDomain, agent); clusterManagementMode = true; } public GroupManagementAgent getGroupManagementAgent(String applicationDomain) { return getGroupManagementAgent(applicationDomain, null); } public GroupManagementAgent getGroupManagementAgent(String applicationDomain, String applicationSubDomain) { if (applicationSubDomain == null) { applicationSubDomain = DEFAULT_SUB_DOMAIN; // default sub-domain since a sub-domain is not specified } Map<String, GroupManagementAgent> groupManagementAgentMap = groupManagementAgents.get(applicationDomain); if (groupManagementAgentMap != null) { return groupManagementAgentMap.get(applicationSubDomain); } return null; } public Set<String> getDomains() { return groupManagementAgents.keySet(); } public StateManager getStateManager() { return contextManager; } public NodeManager getNodeManager() { return configurationManager; } public boolean isCoordinator(){ return coordinator.isCoordinator(); } /** * Initialize the cluster. * * @throws ClusteringFault If initialization fails */ public void init() throws ClusteringFault { log.info("Initializing cluster..."); addRequestBlockingHandlerToInFlows(); primaryMembershipManager = new MembershipManager(configurationContext); channel = new Axis2GroupChannel(); coordinator = new NonBlockingCoordinator(); channel.addInterceptor(coordinator); channel.setHeartbeat(true); channelSender = new ChannelSender(channel, primaryMembershipManager, synchronizeAllMembers()); axis2ChannelListener = new Axis2ChannelListener(configurationContext, configurationManager, contextManager); channel.addChannelListener(axis2ChannelListener); byte[] domain = getClusterDomain(); log.info("Cluster domain: " + new String(domain)); primaryMembershipManager.setDomain(domain); // RpcChannel is a ChannelListener. When the reply to a particular request comes back, it // picks it up. Each RPC is given a UUID, hence can correlate the request-response pair rpcInitRequestHandler = new RpcInitializationRequestHandler(configurationContext); rpcInitChannel = new RpcChannel(TribesUtil.getRpcInitChannelId(domain), channel, rpcInitRequestHandler); if (log.isDebugEnabled()) { log.debug("Created RPC Init Channel for domain " + new String(domain)); } // Initialize RpcChannel used for messaging rpcMessagingHandler = new RpcMessagingHandler(configurationContext); rpcMessagingChannel = new RpcChannel(TribesUtil.getRpcMessagingChannelId(domain), channel, rpcMessagingHandler); if (log.isDebugEnabled()) { log.debug("Created RPC Messaging Channel for domain " + new String(domain)); } setMaximumRetries(); configureMode(domain); configureMembershipScheme(domain, mode.getMembershipManagers()); setMemberInfo(); TribesMembershipListener membershipListener = new TribesMembershipListener(primaryMembershipManager); channel.addMembershipListener(membershipListener); try { channel.start(Channel.DEFAULT); // At this point, this member joins the group String localHost = TribesUtil.getLocalHost(channel); if (localHost.startsWith("127.0.")) { log.warn("Local member advertising its IP address as 127.0.0.1. " + "Remote members will not be able to connect to this member."); } } catch (ChannelException e) { String msg = "Error starting Tribes channel"; log.error(msg, e); throw new ClusteringFault(msg, e); } log.info("Local Member " + TribesUtil.getLocalHost(channel)); TribesUtil.printMembers(primaryMembershipManager); membershipScheme.joinGroup(); configurationContext.getAxisConfiguration().addObservers(new TribesAxisObserver()); ClassLoaderUtil.init(configurationContext.getAxisConfiguration()); // If configuration management is enabled, get the latest config from a neighbour if (configurationManager != null) { configurationManager.setSender(channelSender); initializeSystem(new GetConfigurationCommand()); } // If context replication is enabled, get the latest state from a neighbour if (contextManager != null) { contextManager.setSender(channelSender); axis2ChannelListener.setStateManager(contextManager); initializeSystem(new GetStateCommand()); ClusteringContextListener contextListener = new ClusteringContextListener(channelSender); configurationContext.addContextListener(contextListener); } configurationContext. setNonReplicableProperty(ClusteringConstants.CLUSTER_INITIALIZED, "true"); log.info("Cluster initialization completed."); } public void finalize(){ if (channel != null){ log.info("Stopping Tribes channel..."); try { channel.stop(Channel.DEFAULT); } catch (ChannelException e) { String msg = "Error occurred while stopping channel"; log.error(msg, e); } } } public List<ClusteringCommand> sendMessage(ClusteringMessage message, boolean isRpcMessage) throws ClusteringFault { List<ClusteringCommand> responseList = new ArrayList<ClusteringCommand>(); Member[] members = primaryMembershipManager.getMembers(); if (members.length == 0) { return responseList; } if (isRpcMessage) { try { Response[] responses = rpcMessagingChannel.send(members, message, RpcChannel.ALL_REPLY, Channel.SEND_OPTIONS_SYNCHRONIZED_ACK, 10000); for (Response response : responses) { responseList.add((ClusteringCommand)response.getMessage()); } } catch (ChannelException e) { String msg = "Error occurred while sending RPC message to cluster."; log.error(msg, e); throw new ClusteringFault(msg, e); } } else { try { channel.send(members, message, 10000, new ErrorHandler(){ public void handleError(ChannelException e, UniqueId uniqueId) { log.error("Sending failed " + uniqueId, e ); } public void handleCompletion(UniqueId uniqueId) { if(log.isDebugEnabled()){ log.debug("Sending successful " + uniqueId); } } }); } catch (ChannelException e) { String msg = "Error occurred while sending message to cluster."; log.error(msg, e); throw new ClusteringFault(msg, e); } } return responseList; } private void setMemberInfo() throws ClusteringFault { Properties memberInfo = new Properties(); AxisConfiguration axisConfig = configurationContext.getAxisConfiguration(); TransportInDescription httpTransport = axisConfig.getTransportIn("http"); int portOffset = 0; Parameter param = getParameter(ClusteringConstants.Parameters.AVOID_INITIATION); if(param != null && !JavaUtils.isTrueExplicitly(param.getValue())){ //AvoidInitialization = false, Hence we set the portOffset if(System.getProperty("portOffset") != null){ portOffset = Integer.parseInt(System.getProperty("portOffset")); } } if (httpTransport != null) { Parameter port = httpTransport.getParameter("port"); if (port != null) { memberInfo.put("httpPort", String.valueOf(Integer.valueOf((String)port.getValue()) + portOffset)); } } TransportInDescription httpsTransport = axisConfig.getTransportIn("https"); if (httpsTransport != null) { Parameter port = httpsTransport.getParameter("port"); if (port != null) { memberInfo.put("httpsPort", String.valueOf(Integer.valueOf((String)port.getValue()) + portOffset)); } } Parameter isActiveParam = getParameter(ClusteringConstants.Parameters.IS_ACTIVE); if (isActiveParam != null) { memberInfo.setProperty(ClusteringConstants.Parameters.IS_ACTIVE, (String) isActiveParam.getValue()); } memberInfo.setProperty("hostName", TribesUtil.getLocalHost(getParameter(TribesConstants.LOCAL_MEMBER_HOST))); Parameter propsParam = getParameter("properties"); if(propsParam != null){ OMElement paramEle = propsParam.getParameterElement(); for(Iterator iter = paramEle.getChildrenWithLocalName("property"); iter.hasNext();){ OMElement propEle = (OMElement) iter.next(); OMAttribute nameAttrib = propEle.getAttribute(new QName("name")); if(nameAttrib != null){ String attribName = nameAttrib.getAttributeValue(); attribName = replaceProperty(attribName, memberInfo); OMAttribute valueAttrib = propEle.getAttribute(new QName("value")); if (valueAttrib != null) { String attribVal = valueAttrib.getAttributeValue(); attribVal = replaceProperty(attribVal, memberInfo); memberInfo.setProperty(attribName, attribVal); } } } } memberInfo.remove("hostName"); // this was needed only to populate other properties. No need to send it. ByteArrayOutputStream bout = new ByteArrayOutputStream(); try { memberInfo.store(bout, ""); } catch (IOException e) { String msg = "Cannot store member transport properties in the ByteArrayOutputStream"; log.error(msg, e); throw new ClusteringFault(msg, e); } channel.getMembershipService().setPayload(bout.toByteArray()); } private static String replaceProperty(String text, Properties props) { int indexOfStartingChars = -1; int indexOfClosingBrace; // The following condition deals with properties. // Properties are specified as ${system.property}, // and are assumed to be System properties while (indexOfStartingChars < text.indexOf("${") && (indexOfStartingChars = text.indexOf("${")) != -1 && (indexOfClosingBrace = text.indexOf("}")) != -1) { // Is a property used? String sysProp = text.substring(indexOfStartingChars + 2, indexOfClosingBrace); String propValue = props.getProperty(sysProp); if (propValue == null) { propValue = System.getProperty(sysProp); } if (propValue != null) { text = text.substring(0, indexOfStartingChars) + propValue + text.substring(indexOfClosingBrace + 1); } } return text; } /** * Get the membership scheme applicable to this cluster * * @return The membership scheme. Only "wka" & "multicast" are valid return values. * @throws ClusteringFault If the membershipScheme specified in the axis2.xml file is invalid */ private String getMembershipScheme() throws ClusteringFault { Parameter membershipSchemeParam = getParameter(ClusteringConstants.Parameters.MEMBERSHIP_SCHEME); String mbrScheme = ClusteringConstants.MembershipScheme.MULTICAST_BASED; if (membershipSchemeParam != null) { mbrScheme = ((String) membershipSchemeParam.getValue()).trim(); } if (!mbrScheme.equals(ClusteringConstants.MembershipScheme.MULTICAST_BASED) && !mbrScheme.equals(ClusteringConstants.MembershipScheme.WKA_BASED)) { String msg = "Invalid membership scheme '" + mbrScheme + "'. Supported schemes are " + ClusteringConstants.MembershipScheme.MULTICAST_BASED + " & " + ClusteringConstants.MembershipScheme.WKA_BASED; log.error(msg); throw new ClusteringFault(msg); } return mbrScheme; } /** * Get the clustering domain to which this node belongs to * * @return The clustering domain to which this node belongs to */ private byte[] getClusterDomain() { Parameter domainParam = getParameter(ClusteringConstants.Parameters.DOMAIN); byte[] domain; if (domainParam != null) { domain = ((String) domainParam.getValue()).getBytes(); } else { domain = ClusteringConstants.DEFAULT_DOMAIN.getBytes(); } return domain; } /** * Set the maximum number of retries, if message sending to a particular node fails */ private void setMaximumRetries() { Parameter maxRetriesParam = getParameter(TribesConstants.MAX_RETRIES); int maxRetries = 10; if (maxRetriesParam != null) { maxRetries = Integer.parseInt((String) maxRetriesParam.getValue()); } ReplicationTransmitter replicationTransmitter = (ReplicationTransmitter) channel.getChannelSender(); MultiPointSender multiPointSender = replicationTransmitter.getTransport(); multiPointSender.setMaxRetryAttempts(maxRetries); } /** * A RequestBlockingHandler, which is an implementation of * {@link org.apache.axis2.engine.Handler} is added to the InFlow & InFaultFlow. This handler * is used for rejecting Web service requests until this node has been initialized. This handler * can also be used for rejecting requests when this node is reinitializing or is in an * inconsistent state (which can happen when a configuration change is taking place). */ private void addRequestBlockingHandlerToInFlows() { AxisConfiguration axisConfig = configurationContext.getAxisConfiguration(); for (Object o : axisConfig.getInFlowPhases()) { Phase phase = (Phase) o; if (phase instanceof DispatchPhase) { RequestBlockingHandler requestBlockingHandler = new RequestBlockingHandler(); if (!phase.getHandlers().contains(requestBlockingHandler)) { PhaseRule rule = new PhaseRule("Dispatch"); rule.setAfter("SOAPMessageBodyBasedDispatcher"); rule.setBefore("InstanceDispatcher"); HandlerDescription handlerDesc = requestBlockingHandler.getHandlerDesc(); handlerDesc.setHandler(requestBlockingHandler); handlerDesc.setName(ClusteringConstants.REQUEST_BLOCKING_HANDLER); handlerDesc.setRules(rule); phase.addHandler(requestBlockingHandler); log.debug("Added " + ClusteringConstants.REQUEST_BLOCKING_HANDLER + " between SOAPMessageBodyBasedDispatcher & InstanceDispatcher to InFlow"); break; } } } for (Object o : axisConfig.getInFaultFlowPhases()) { Phase phase = (Phase) o; if (phase instanceof DispatchPhase) { RequestBlockingHandler requestBlockingHandler = new RequestBlockingHandler(); if (!phase.getHandlers().contains(requestBlockingHandler)) { PhaseRule rule = new PhaseRule("Dispatch"); rule.setAfter("SOAPMessageBodyBasedDispatcher"); rule.setBefore("InstanceDispatcher"); HandlerDescription handlerDesc = requestBlockingHandler.getHandlerDesc(); handlerDesc.setHandler(requestBlockingHandler); handlerDesc.setName(ClusteringConstants.REQUEST_BLOCKING_HANDLER); handlerDesc.setRules(rule); phase.addHandler(requestBlockingHandler); log.debug("Added " + ClusteringConstants.REQUEST_BLOCKING_HANDLER + " between SOAPMessageBodyBasedDispatcher & InstanceDispatcher to InFaultFlow"); break; } } } } private void configureMode(byte[] domain) { if (clusterManagementMode) { mode = new ClusterManagementMode(domain, groupManagementAgents, primaryMembershipManager); for (Map<String, GroupManagementAgent> agents : groupManagementAgents.values()) { for (GroupManagementAgent agent : agents.values()) { if (agent instanceof DefaultGroupManagementAgent) { ((DefaultGroupManagementAgent) agent).setSender(channelSender); } } } } else { mode = new ApplicationMode(domain, primaryMembershipManager); } mode.init(channel); } /** * Handle specific configurations related to different membership management schemes. * * @param localDomain The clustering loadBalancerDomain to which this member belongs to * @param membershipManagers MembershipManagers for different domains * @throws ClusteringFault If the membership scheme is invalid, or if an error occurs * while configuring membership scheme */ private void configureMembershipScheme(byte[] localDomain, List<MembershipManager> membershipManagers) throws ClusteringFault { MembershipListener membershipListener; Parameter parameter = getParameter(ClusteringConstants.Parameters.MEMBERSHIP_LISTENER); if (parameter != null) { OMElement paramEle = parameter.getParameterElement(); String clazz = paramEle.getFirstChildWithName(new QName("class")).getText().trim(); try { membershipListener = (MembershipListener) Class.forName(clazz).newInstance(); } catch (Exception e) { String msg = "Cannot instantiate MembershipListener " + clazz; log.error(msg, e); throw new ClusteringFault(msg, e); } OMElement propsEle = paramEle.getFirstChildWithName(new QName("properties")); if (propsEle != null) { for (Iterator iter = propsEle.getChildElements(); iter.hasNext();) { OMElement propEle = (OMElement) iter.next(); OMAttribute nameAttrib = propEle.getAttribute(new QName("name")); if (nameAttrib != null) { String name = nameAttrib.getAttributeValue(); setInstanceProperty(name, propEle.getText().trim(), membershipListener); } } } } String scheme = getMembershipScheme(); log.info("Using " + scheme + " based membership management scheme"); if (scheme.equals(ClusteringConstants.MembershipScheme.WKA_BASED)) { membershipScheme = new WkaBasedMembershipScheme(channel, mode, membershipManagers, primaryMembershipManager, parameters, localDomain, members, getBooleanParam(ClusteringConstants.Parameters.ATMOST_ONCE_MSG_SEMANTICS), getBooleanParam(ClusteringConstants.Parameters.PRESERVE_MSG_ORDER)); } else if (scheme.equals(ClusteringConstants.MembershipScheme.MULTICAST_BASED)) { membershipScheme = new MulticastBasedMembershipScheme(channel, mode, parameters, localDomain, getBooleanParam(ClusteringConstants.Parameters.ATMOST_ONCE_MSG_SEMANTICS), getBooleanParam(ClusteringConstants.Parameters.PRESERVE_MSG_ORDER)); } else { String msg = "Invalid membership scheme '" + scheme + "'. Supported schemes are multicast & wka"; log.error(msg); throw new ClusteringFault(msg); } membershipScheme.init(); } private boolean getBooleanParam(String name) { boolean result = false; Parameter parameter = getParameter(name); if (parameter != null) { Object value = parameter.getValue(); if (value != null) { result = Boolean.valueOf(((String) value).trim()); } } return result; } /** * Find and invoke the setter method with the name of form setXXX passing in the value given * on the POJO object * * @param name name of the setter field * @param val value to be set * @param obj POJO instance * @throws ClusteringFault If an error occurs while setting the property */ private void setInstanceProperty(String name, Object val, Object obj) throws ClusteringFault { String mName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1); Method method; try { Method[] methods = obj.getClass().getMethods(); boolean invoked = false; for (Method method1 : methods) { if (mName.equals(method1.getName())) { Class[] params = method1.getParameterTypes(); if (params.length != 1) { handleException("Did not find a setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter"); } else if (val instanceof String) { String value = (String) val; if (params[0].equals(String.class)) { method = obj.getClass().getMethod(mName, String.class); method.invoke(obj, new String[]{value}); } else if (params[0].equals(int.class)) { method = obj.getClass().getMethod(mName, int.class); method.invoke(obj, new Integer[]{new Integer(value)}); } else if (params[0].equals(long.class)) { method = obj.getClass().getMethod(mName, long.class); method.invoke(obj, new Long[]{new Long(value)}); } else if (params[0].equals(float.class)) { method = obj.getClass().getMethod(mName, float.class); method.invoke(obj, new Float[]{new Float(value)}); } else if (params[0].equals(double.class)) { method = obj.getClass().getMethod(mName, double.class); method.invoke(obj, new Double[]{new Double(value)}); } else if (params[0].equals(boolean.class)) { method = obj.getClass().getMethod(mName, boolean.class); method.invoke(obj, new Boolean[]{Boolean.valueOf(value)}); } else { handleException("Did not find a setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter"); } } else { if (params[0].equals(OMElement.class)) { method = obj.getClass().getMethod(mName, OMElement.class); method.invoke(obj, new OMElement[]{(OMElement) val}); } } invoked = true; } } if (!invoked) { handleException("Did not find a setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter"); } } catch (InvocationTargetException e) { handleException("Error invoking setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter", e); } catch (NoSuchMethodException e) { handleException("Error invoking setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter", e); } catch (IllegalAccessException e) { handleException("Error invoking setter method named : " + mName + "() that takes a single String, int, long, float, double " + "or boolean parameter", e); } } private void handleException(String msg, Exception e) throws ClusteringFault { log.error(msg, e); throw new ClusteringFault(msg, e); } private void handleException(String msg) throws ClusteringFault { log.error(msg); throw new ClusteringFault(msg); } /** * Get some information from a neighbour. This information will be used by this node to * initialize itself * <p/> * rpcInitChannel is The utility for sending RPC style messages to the channel * * @param command The control command to send * @throws ClusteringFault If initialization code failed on this node */ private void initializeSystem(ControlCommand command) throws ClusteringFault { // If there is at least one member in the cluster, // get the current initialization info from a member int numberOfTries = 0; // Don't keep on trying indefinitely // Keep track of members to whom we already sent an initialization command // Do not send another request to these members List<String> sentMembersList = new ArrayList<String>(); sentMembersList.add(TribesUtil.getLocalHost(channel)); Member[] members = primaryMembershipManager.getMembers(); if (members.length == 0) { return; } while (members.length > 0 && numberOfTries < 5) { Member member = (numberOfTries == 0) ? primaryMembershipManager.getLongestLivingMember() : // First try to get from the longest member alive primaryMembershipManager.getRandomMember(); // Else get from a random member String memberHost = TribesUtil.getName(member); log.info("Trying to send initialization request to " + memberHost); try { if (!sentMembersList.contains(memberHost)) { Response[] responses; // do { responses = rpcInitChannel.send(new Member[]{member}, command, RpcChannel.FIRST_REPLY, Channel.SEND_OPTIONS_ASYNCHRONOUS | Channel.SEND_OPTIONS_BYTE_MESSAGE, 10000); if (responses.length == 0) { try { Thread.sleep(500); } catch (InterruptedException ignored) { } } // TODO: If we do not get a response within some time, try to recover from this fault // } // while (responses.length == 0 || responses[0] == null || responses[0].getMessage() == null); // TODO: #### We will need to check this if (responses.length != 0 && responses[0] != null && responses[0].getMessage() != null) { ((ControlCommand) responses[0].getMessage()).execute(configurationContext); // Do the initialization break; } } } catch (Exception e) { log.error("Cannot get initialization information from " + memberHost + ". Will retry in 2 secs.", e); sentMembersList.add(memberHost); try { Thread.sleep(2000); } catch (InterruptedException ignored) { log.debug("Interrupted", ignored); } } numberOfTries++; members = primaryMembershipManager.getMembers(); if (numberOfTries >= members.length) { break; } } } public void setNodeManager(NodeManager nodeManager) { this.configurationManager = (DefaultNodeManager) nodeManager; this.configurationManager.setSender(channelSender); } public void setStateManager(StateManager stateManager) { this.contextManager = (DefaultStateManager) stateManager; this.contextManager.setSender(channelSender); } public void addParameter(Parameter param) throws AxisFault { parameters.put(param.getName(), param); } public void deserializeParameters(OMElement parameterElement) throws AxisFault { throw new UnsupportedOperationException(); } public Parameter getParameter(String name) { return parameters.get(name); } public ArrayList getParameters() { ArrayList<Parameter> list = new ArrayList<Parameter>(); for (String msg : parameters.keySet()) { list.add(parameters.get(msg)); } return list; } public boolean isParameterLocked(String parameterName) { Parameter parameter = parameters.get(parameterName); return parameter != null && parameter.isLocked(); } public void removeParameter(Parameter param) throws AxisFault { parameters.remove(param.getName()); } /** * Shutdown the cluster. This member will leave the cluster when this method is called. * * @throws ClusteringFault If an error occurs while shutting down */ public void shutdown() throws ClusteringFault { log.debug("Enter: TribesClusteringAgent::shutdown"); if (channel != null) { try { channel.removeChannelListener(rpcInitChannel); channel.removeChannelListener(rpcMessagingChannel); channel.removeChannelListener(axis2ChannelListener); channel.stop(Channel.DEFAULT); } catch (ChannelException e) { if (log.isDebugEnabled()) { log.debug("Exit: TribesClusteringAgent::shutdown"); } throw new ClusteringFault(e); } } log.debug("Exit: TribesClusteringAgent::shutdown"); } public void setConfigurationContext(ConfigurationContext configurationContext) { this.configurationContext = configurationContext; if (rpcInitRequestHandler != null) { rpcInitRequestHandler.setConfigurationContext(configurationContext); } if (rpcMessagingHandler!= null) { rpcMessagingHandler.setConfigurationContext(configurationContext); } if (axis2ChannelListener != null) { axis2ChannelListener.setConfigurationContext(configurationContext); } if (configurationManager != null) { configurationManager.setConfigurationContext(configurationContext); } if (contextManager != null) { contextManager.setConfigurationContext(configurationContext); } } /** * Method to check whether all members in the cluster have to be kept in sync at all times. * Typically, this will require each member in the cluster to ACKnowledge receipt of a * particular message, which may have a significant performance hit. * * @return true - if all members in the cluster should be kept in sync at all times, false * otherwise */ public boolean synchronizeAllMembers() { Parameter syncAllParam = getParameter(ClusteringConstants.Parameters.SYNCHRONIZE_ALL_MEMBERS); return syncAllParam == null || Boolean.parseBoolean((String) syncAllParam.getValue()); } }