All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.activemq.artemis.core.client.impl.Topology Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 32.0.0.Final
Show newest version
/**
 * 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.activemq.artemis.core.client.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ClusterTopologyListener;
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
import org.apache.activemq.artemis.spi.core.remoting.Connector;

public final class Topology
{

   private final Set topologyListeners;

   private final Executor executor;

   /**
    * Used to debug operations.
    * 

* Someone may argue this is not needed. But it's impossible to debug anything related to * topology without knowing what node or what object missed a Topology update. Hence I added some * information to locate debugging here. */ private volatile Object owner; /** * topology describes the other cluster nodes that this server knows about: * * keys are node IDs * values are a pair of live/backup transport configurations */ private final Map topology; private Map mapDelete; private static final class DirectExecutor implements Executor { public void execute(final Runnable runnable) { runnable.run(); } } public Topology(final Object owner) { this(owner, new DirectExecutor()); } public Topology(final Object owner, final Executor executor) { this.topologyListeners = new HashSet<>(); this.topology = new ConcurrentHashMap<>(); if (executor == null) { throw new IllegalArgumentException("Executor is required"); } this.executor = executor; this.owner = owner; if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace("Topology@" + Integer.toHexString(System.identityHashCode(this)) + " CREATE", new Exception("trace")); } } /** * It will remove all elements as if it haven't received anyone from the server. */ public void clear() { topology.clear(); } public void addClusterTopologyListener(final ClusterTopologyListener listener) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::Adding topology listener " + listener, new Exception("Trace")); } synchronized (topologyListeners) { topologyListeners.add(listener); } this.sendTopology(listener); } public void removeClusterTopologyListener(final ClusterTopologyListener listener) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::Removing topology listener " + listener, new Exception("Trace")); } synchronized (topologyListeners) { topologyListeners.remove(listener); } } /** This is called by the server when the node is activated from backup state. It will always succeed */ public void updateAsLive(final String nodeId, final TopologyMemberImpl memberInput) { synchronized (this) { if (ActiveMQClientLogger.LOGGER.isDebugEnabled()) { ActiveMQClientLogger.LOGGER.debug(this + "::node " + nodeId + "=" + memberInput); } memberInput.setUniqueEventID(System.currentTimeMillis()); topology.remove(nodeId); topology.put(nodeId, memberInput); sendMemberUp(nodeId, memberInput); } } /** * After the node is started, it will resend the notifyLive a couple of times to avoid gossip between two servers * @param nodeId */ public void resendNode(final String nodeId) { synchronized (this) { TopologyMemberImpl memberInput = topology.get(nodeId); if (memberInput != null) { memberInput.setUniqueEventID(System.currentTimeMillis()); sendMemberUp(nodeId, memberInput); } } } /** This is called by the server when the node is activated from backup state. It will always succeed */ public TopologyMemberImpl updateBackup(final TopologyMemberImpl memberInput) { final String nodeId = memberInput.getNodeId(); if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::updateBackup::" + nodeId + ", memberInput=" + memberInput); } synchronized (this) { TopologyMemberImpl currentMember = getMember(nodeId); if (currentMember == null) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace("There's no live to be updated on backup update, node=" + nodeId + " memberInput=" + memberInput, new Exception("trace")); } currentMember = memberInput; topology.put(nodeId, currentMember); } TopologyMemberImpl newMember = new TopologyMemberImpl(nodeId, currentMember.getBackupGroupName(), currentMember.getScaleDownGroupName(), currentMember.getLive(), memberInput.getBackup()); newMember.setUniqueEventID(System.currentTimeMillis()); topology.remove(nodeId); topology.put(nodeId, newMember); sendMemberUp(nodeId, newMember); return newMember; } } /** * @param uniqueEventID an unique identifier for when the change was made. We will use current * time millis for starts, and a ++ of that number for shutdown. * @param nodeId * @param memberInput * @return {@code true} if an update did take place. Note that backups are *always* updated. */ public boolean updateMember(final long uniqueEventID, final String nodeId, final TopologyMemberImpl memberInput) { Long deleteTme = getMapDelete().get(nodeId); if (deleteTme != null && uniqueEventID != 0 && uniqueEventID < deleteTme) { ActiveMQClientLogger.LOGGER.debug("Update uniqueEvent=" + uniqueEventID + ", nodeId=" + nodeId + ", memberInput=" + memberInput + " being rejected as there was a delete done after that"); return false; } synchronized (this) { TopologyMemberImpl currentMember = topology.get(nodeId); if (currentMember == null) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::NewMemberAdd nodeId=" + nodeId + " member = " + memberInput, new Exception("trace")); } memberInput.setUniqueEventID(uniqueEventID); topology.put(nodeId, memberInput); sendMemberUp(nodeId, memberInput); return true; } if (uniqueEventID > currentMember.getUniqueEventID()) { TopologyMemberImpl newMember = new TopologyMemberImpl(nodeId, memberInput.getBackupGroupName(), memberInput.getScaleDownGroupName(), memberInput.getLive(), memberInput.getBackup()); if (newMember.getLive() == null && currentMember.getLive() != null) { newMember.setLive(currentMember.getLive()); } if (newMember.getBackup() == null && currentMember.getBackup() != null) { newMember.setBackup(currentMember.getBackup()); } if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::updated currentMember=nodeID=" + nodeId + ", currentMember=" + currentMember + ", memberInput=" + memberInput + "newMember=" + newMember, new Exception("trace")); } newMember.setUniqueEventID(uniqueEventID); topology.remove(nodeId); topology.put(nodeId, newMember); sendMemberUp(nodeId, newMember); return true; } /* * always add the backup, better to try to reconnect to something that's not there then to * not know about it at all */ if (currentMember.getBackup() == null && memberInput.getBackup() != null) { currentMember.setBackup(memberInput.getBackup()); } return false; } } /** * @param nodeId * @param memberToSend */ private void sendMemberUp(final String nodeId, final TopologyMemberImpl memberToSend) { final ArrayList copy = copyListeners(); if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + "::prepare to send " + nodeId + " to " + copy.size() + " elements"); } if (copy.size() > 0) { executor.execute(new Runnable() { public void run() { for (ClusterTopologyListener listener : copy) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(Topology.this + " informing " + listener + " about node up = " + nodeId + " connector = " + memberToSend.getConnector()); } try { listener.nodeUP(memberToSend, false); } catch (Throwable e) { ActiveMQClientLogger.LOGGER.errorSendingTopology(e); } } } }); } } /** * @return */ private ArrayList copyListeners() { ArrayList listenersCopy; synchronized (topologyListeners) { listenersCopy = new ArrayList<>(topologyListeners); } return listenersCopy; } boolean removeMember(final long uniqueEventID, final String nodeId) { TopologyMemberImpl member; synchronized (this) { member = topology.get(nodeId); if (member != null) { if (member.getUniqueEventID() > uniqueEventID) { ActiveMQClientLogger.LOGGER.debug("The removeMember was issued before the node " + nodeId + " was started, ignoring call"); member = null; } else { getMapDelete().put(nodeId, uniqueEventID); member = topology.remove(nodeId); } } } if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace("removeMember " + this + " removing nodeID=" + nodeId + ", result=" + member + ", size = " + topology.size(), new Exception("trace")); } if (member != null) { final ArrayList copy = copyListeners(); executor.execute(new Runnable() { public void run() { for (ClusterTopologyListener listener : copy) { if (ActiveMQClientLogger.LOGGER.isTraceEnabled()) { ActiveMQClientLogger.LOGGER.trace(this + " informing " + listener + " about node down = " + nodeId); } try { listener.nodeDown(uniqueEventID, nodeId); } catch (Exception e) { ActiveMQClientLogger.LOGGER.errorSendingTopologyNodedown(e); } } } }); } return member != null; } public synchronized void sendTopology(final ClusterTopologyListener listener) { if (ActiveMQClientLogger.LOGGER.isDebugEnabled()) { ActiveMQClientLogger.LOGGER.debug(this + " is sending topology to " + listener); } executor.execute(new Runnable() { public void run() { int count = 0; final Map copy; synchronized (Topology.this) { copy = new HashMap(topology); } for (Map.Entry entry : copy.entrySet()) { if (ActiveMQClientLogger.LOGGER.isDebugEnabled()) { ActiveMQClientLogger.LOGGER.debug(Topology.this + " sending " + entry.getKey() + " / " + entry.getValue().getConnector() + " to " + listener); } listener.nodeUP(entry.getValue(), ++count == copy.size()); } } }); } public synchronized TopologyMemberImpl getMember(final String nodeID) { return topology.get(nodeID); } public synchronized TopologyMemberImpl getMember(final TransportConfiguration configuration) { for (TopologyMemberImpl member : topology.values()) { if (member.isMember(configuration)) { return member; } } return null; } public synchronized boolean isEmpty() { return topology.isEmpty(); } public Collection getMembers() { ArrayList members; synchronized (this) { members = new ArrayList<>(topology.values()); } return members; } synchronized int nodes() { int count = 0; for (TopologyMemberImpl member : topology.values()) { if (member.getLive() != null) { count++; } if (member.getBackup() != null) { count++; } } return count; } public synchronized String describe() { return describe(""); } private synchronized String describe(final String text) { StringBuilder desc = new StringBuilder(text + "topology on " + this + ":\n"); for (Entry entry : new HashMap<>(topology).entrySet()) { desc.append("\t").append(entry.getKey()).append(" => ").append(entry.getValue()).append("\n"); } desc.append("\t" + "nodes=").append(nodes()).append("\t").append("members=").append(members()); if (topology.isEmpty()) { desc.append("\tEmpty"); } return desc.toString(); } private int members() { return topology.size(); } /** The owner exists mainly for debug purposes. * When enabling logging and tracing, the Topology updates will include the owner, what will enable to identify * what instances are receiving the updates, what will enable better debugging.*/ public void setOwner(final Object owner) { this.owner = owner; } public TransportConfiguration getBackupForConnector(final Connector connector) { for (TopologyMemberImpl member : topology.values()) { if (member.getLive() != null && connector.isEquivalent(member.getLive().getParams())) { return member.getBackup(); } } return null; } @Override public String toString() { if (owner == null) { return "Topology@" + Integer.toHexString(System.identityHashCode(this)); } return "Topology@" + Integer.toHexString(System.identityHashCode(this)) + "[owner=" + owner + "]"; } private synchronized Map getMapDelete() { if (mapDelete == null) { mapDelete = new ConcurrentHashMap<>(); } return mapDelete; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy