org.jgroups.protocols.TP 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).
package org.jgroups.protocols;
import org.jgroups.*;
import org.jgroups.annotations.*;
import org.jgroups.blocks.LazyRemovalCache;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.conf.PropertyConverters;
import org.jgroups.jmx.AdditionalJmxObjects;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.stack.*;
import org.jgroups.util.Bits;
import org.jgroups.util.ThreadFactory;
import org.jgroups.util.UUID;
import org.jgroups.util.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
/**
* Generic transport - specific implementations should extend this abstract class.
* Features which are provided to the subclasses include
*
* - version checking
*
- marshalling and unmarshalling
*
- message bundling (handling single messages, and message lists)
*
- incoming packet handler
*
* A subclass has to override
*
* - {@link #sendMulticast(byte[], int, int)}
*
- {@link #sendUnicast(org.jgroups.PhysicalAddress, byte[], int, int)}
*
- {@link #init()}
*
- {@link #start()}: subclasses must call super.start() after they initialize themselves
* (e.g., created their sockets).
*
- {@link #stop()}: subclasses must call super.stop() after they deinitialized themselves
*
- {@link #destroy()}
*
* The create() or start() method has to create a local address.
* The {@link #receive(Address, byte[], int, int)} method must
* be called by subclasses when a unicast or multicast message has been received.
* @author Bela Ban
*/
@MBean(description="Transport protocol")
public abstract class TP extends Protocol implements DiagnosticsHandler.ProbeHandler, AdditionalJmxObjects {
public static final byte LIST=1; // we have a list of messages rather than a single message when set
public static final byte MULTICAST=2; // message is a multicast (versus a unicast) message when set
public static final int MSG_OVERHEAD=Global.SHORT_SIZE + Global.BYTE_SIZE; // version + flags
protected static final long MIN_WAIT_BETWEEN_DISCOVERIES=TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS); // ns
/* ------------------------------------------ JMX and Properties ------------------------------------------ */
@LocalAddress
@Property(name="bind_addr",
description="The bind address which should be used by this transport. The following special values " +
"are also recognized: GLOBAL, SITE_LOCAL, LINK_LOCAL, NON_LOOPBACK, match-interface, match-host, match-address",
defaultValueIPv4=Global.NON_LOOPBACK_ADDRESS, defaultValueIPv6=Global.NON_LOOPBACK_ADDRESS,
systemProperty={Global.BIND_ADDR},writable=false)
protected InetAddress bind_addr;
@Property(description="Use IP addresses (IpAddressUUID) instead of UUIDs as addresses. This is currently not " +
"compatible with RELAY2: disable if RELAY2 is used.")
protected boolean use_ip_addrs;
@Property(description="Use \"external_addr\" if you have hosts on different networks, behind " +
"firewalls. On each firewall, set up a port forwarding rule (sometimes called \"virtual server\") to " +
"the local IP (e.g. 192.168.1.100) of the host then on each host, set \"external_addr\" TCP transport " +
"parameter to the external (public IP) address of the firewall.",
systemProperty=Global.EXTERNAL_ADDR,writable=false)
protected InetAddress external_addr;
@Property(description="Used to map the internal port (bind_port) to an external port. Only used if > 0",
systemProperty=Global.EXTERNAL_PORT,writable=false)
protected int external_port;
@ManagedAttribute(description="tracing is enabled or disabled for the given log",writable=true)
protected boolean is_trace=log.isTraceEnabled();
@Property(description="If true, the transport should use all available interfaces to receive multicast messages")
protected boolean receive_on_all_interfaces;
/**
* List of interfaces to receive multicasts on. The multicast receive socket will listen
* on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g.
* "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to
* an interface once. If this property is set, it overrides receive_on_all_interfaces.
*/
@Property(converter=PropertyConverters.NetworkInterfaceList.class,
description="Comma delimited list of interfaces (IP addresses or interface names) to receive multicasts on")
protected List receive_interfaces;
@Property(description="Max number of elements in the logical address cache before eviction starts")
protected int logical_addr_cache_max_size=2000;
@Property(description="Time (in ms) after which entries in the logical address cache marked as removable " +
"can be removed. 0 never removes any entries (not recommended)")
protected long logical_addr_cache_expiration=360000;
@Property(description="Interval (in ms) at which the reaper task scans logical_addr_cache and removes entries " +
"marked as removable. 0 disables reaping.")
protected long logical_addr_cache_reaper_interval=60000;
/** The port to which the transport binds. 0 means to bind to any (ephemeral) port. See also {@link #port_range} */
@Property(description="The port to which the transport binds. Default of 0 binds to any (ephemeral) port." +
" See also port_range",systemProperty={Global.BIND_PORT},writable=false)
protected int bind_port;
@Property(description="The range of valid ports: [bind_port .. bind_port+port_range ]. " +
"0 only binds to bind_port and fails if taken")
protected int port_range=10; // 27-6-2003 bgooren, Only try one port by default
@Property(description="Whether or not to make a copy of a message before looping it back up. Don't use this; might " +
"get removed without warning")
protected boolean loopback_copy;
@Property(description="Loop back the message on a separate thread or use the current thread. Don't use this; " +
"might get removed without warning")
protected boolean loopback_separate_thread=true;
@Property(description="The fully qualified name of a class implementing MessageProcessingPolicy")
protected String message_processing_policy;
@Property(name="message_processing_policy.max_buffer_size",
description="Max number of messages buffered for consumption of the delivery thread in MaxOneThreadPerSender. 0 creates an unbounded buffer")
protected int msg_processing_max_buffer_size=5000;
@Property(description="Thread naming pattern for threads in this channel. Valid values are \"pcl\": " +
"\"p\": includes the thread name, e.g. \"Incoming thread-1\", \"UDP ucast receiver\", " +
"\"c\": includes the cluster name, e.g. \"MyCluster\", " +
"\"l\": includes the local address of the current member, e.g. \"192.168.5.1:5678\"")
protected String thread_naming_pattern="cl";
@Property(name="thread_pool.use_fork_join_pool",description="If enabled, a ForkJoinPool will be used rather than a ThreadPoolExecutor")
protected boolean use_fork_join_pool;
@Property(name="thread_pool.use_common_fork_join_pool",
description="If true, the common fork-join pool will be used; otherwise a custom ForkJoinPool will be created")
protected boolean use_common_fork_join_pool;
@Property(description="Create fibers (if true) or regular threads (if false). This requires Java 15/Loom. " +
"If not present, use_fibers will be set to false and regular threads will be created. Note that the ThreadFactory" +
"needs to support this (DefaultThreadFactory does)")
protected boolean use_fibers;
@Property(name="thread_pool.enabled",description="Enable or disable the thread pool")
protected boolean thread_pool_enabled=true;
@Property(name="thread_pool.min_threads",description="Minimum thread pool size for the thread pool")
protected int thread_pool_min_threads=0;
@Property(name="thread_pool.max_threads",description="Maximum thread pool size for the thread pool")
protected int thread_pool_max_threads=100;
@Property(name="thread_pool.keep_alive_time",description="Timeout in milliseconds to remove idle threads from pool")
protected long thread_pool_keep_alive_time=30000;
@Property(description="When an internal message cannot be processed because of a full internal pool, a new thread "
+ "is created to process the message. Setting this value to false disables this, and the message will be " +
"discarded (like regular messages)")
protected boolean spawn_thread_on_full_pool;
@Property(description="Interval (in ms) at which the time service updates its timestamp. 0 disables the time service")
protected long time_service_interval=500;
@Property(description="Switch to enable diagnostic probing. Default is true")
protected boolean enable_diagnostics=true;
@Property(description="Use a multicast socket to listen for probe requests (ignored if enable_diagnostics is false)")
protected boolean diag_enable_udp=true;
@Property(description="Use a TCP socket to listen for probe requests (ignored if enable_diagnostics is false)")
protected boolean diag_enable_tcp;
@Property(description="Multicast address for diagnostic probing. Used when diag_enable_udp is true",
defaultValueIPv4="224.0.75.75",defaultValueIPv6="ff0e::0:75:75")
protected InetAddress diagnostics_addr;
@Property(description="Bind address for diagnostic probing. Used when diag_enable_tcp is true")
protected InetAddress diagnostics_bind_addr;
@Property(converter=PropertyConverters.NetworkInterfaceList.class,
description="Comma delimited list of interfaces (IP addresses or interface names) that the " +
"diagnostics multicast socket should bind to")
protected List diagnostics_bind_interfaces;
@Property(description="Port for diagnostic probing. Default is 7500")
protected int diagnostics_port=7500;
@Property(description="The number of ports to be probed for an available port (TCP)")
protected int diagnostics_port_range=50;
@Property(description="TTL of the diagnostics multicast socket")
protected int diagnostics_ttl=8;
@Property(description="Authorization passcode for diagnostics. If specified every probe query will be authorized")
protected String diagnostics_passcode;
/** Whether or not warnings about messages from different groups are logged - private flag, not for common use */
@Property(description="whether or not warnings about messages from different groups are logged")
protected boolean log_discard_msgs=true;
@Property(description="whether or not warnings about messages from members with a different version are discarded")
protected boolean log_discard_msgs_version=true;
@Property(description="Timeout (in ms) to determine how long to wait until a request to fetch the physical address " +
"for a given logical address will be sent again. Subsequent requests for the same physical address will therefore " +
"be spaced at least who_has_cache_timeout ms apart")
protected long who_has_cache_timeout=2000;
@Property(description="Time during which identical warnings about messages from a member with a different version " +
"will be suppressed. 0 disables this (every warning will be logged). Setting the log level to ERROR also " +
"disables this.")
protected long suppress_time_different_version_warnings=60000;
@Property(description="Time during which identical warnings about messages from a member from a different cluster " +
"will be suppressed. 0 disables this (every warning will be logged). Setting the log level to ERROR also " +
"disables this.")
protected long suppress_time_different_cluster_warnings=60000;
@Property(description="The number of times a thread pool needs to be full before a thread dump is logged")
protected int thread_dumps_threshold=1;
@Property(description="Path to which the thread dump will be written. Ignored if null",
systemProperty="jgroups.threaddump.path")
protected String thread_dump_path;
// Incremented when a message is rejected due to a full thread pool. When this value exceeds thread_dumps_threshold,
// the threads will be dumped at FATAL level, and thread_dumps will be reset to 0
protected final AtomicInteger thread_dumps=new AtomicInteger();
/**
* Maximum number of bytes for messages to be queued until they are sent.
* This value needs to be smaller than the largest datagram packet size in case of UDP
*/
@Property(name="max_bundle_size", description="Maximum number of bytes for messages to be queued until they are sent")
protected int max_bundle_size=64000;
@Property(description="The type of bundler used (\"ring-buffer\", \"transfer-queue\" (default), \"sender-sends\" or " +
"\"no-bundler\") or the fully qualified classname of a Bundler implementation")
protected String bundler_type="transfer-queue";
@Property(description="The max number of elements in a bundler if the bundler supports size limitations")
protected int bundler_capacity=16384;
@Property(description="Number of spins before a real lock is acquired")
protected int bundler_num_spins=5;
@Property(description="The wait strategy for a RingBuffer")
protected String bundler_wait_strategy="park";
@ManagedAttribute(description="Fully qualified classname of bundler")
public String getBundlerClass() {
return bundler != null? bundler.getClass().getName() : "null";
}
public T setMaxBundleSize(int size) {
if(size <= 0)
throw new IllegalArgumentException("max_bundle_size (" + size + ") is <= 0");
max_bundle_size=size;
return (T)this;
}
public final int getMaxBundleSize() {return max_bundle_size;}
public int getBundlerCapacity() {return bundler_capacity;}
public T setBundlerCapacity(int c) {this.bundler_capacity=c; return (T)this;}
public int getMessageProcessingMaxBufferSize() {return msg_processing_max_buffer_size;}
public boolean useFibers() {return use_fibers;}
public int getThreadDumpsThreshold() {return thread_dumps_threshold;}
public T setThreadDumpsThreshold(int t) {this.thread_dumps_threshold=t; return (T)this;}
@ManagedAttribute public int getBundlerBufferSize() {
if(bundler instanceof TransferQueueBundler)
return ((TransferQueueBundler)bundler).getBufferSize();
return bundler != null? bundler.size() : 0;
}
@ManagedAttribute(description="The wait strategy for a RingBuffer")
public String bundlerWaitStrategy() {
return bundler instanceof RingBufferBundler? ((RingBufferBundler)bundler).waitStrategy() : bundler_wait_strategy;
}
@ManagedAttribute(description="Sets the wait strategy in the RingBufferBundler. Allowed values are \"spin\", " +
"\"yield\", \"park\", \"spin-park\" and \"spin-yield\" or a fully qualified classname")
public T bundlerWaitStrategy(String strategy) {
if(bundler instanceof RingBufferBundler) {
((RingBufferBundler)bundler).waitStrategy(strategy);
this.bundler_wait_strategy=strategy;
}
else
this.bundler_wait_strategy=strategy;
return (T)this;
}
@ManagedAttribute(description="Number of spins before a real lock is acquired")
public int bundlerNumSpins() {
return bundler instanceof RingBufferBundler? ((RingBufferBundler)bundler).numSpins() : bundler_num_spins;
}
@ManagedAttribute(description="Sets the number of times a thread spins until a real lock is acquired")
public T bundlerNumSpins(int spins) {
this.bundler_num_spins=spins;
if(bundler instanceof RingBufferBundler)
((RingBufferBundler)bundler).numSpins(spins);
return (T)this;
}
@ManagedAttribute(description="Is the logical_addr_cache reaper task running")
public boolean isLogicalAddressCacheReaperRunning() {
return logical_addr_cache_reaper != null && !logical_addr_cache_reaper.isDone();
}
@ManagedAttribute(description="Returns the average batch size of received batches")
public String getAvgBatchSize() {
return avg_batch_size.toString();
}
public AverageMinMax avgBatchSize() {return avg_batch_size;}
public T setThreadPoolMinThreads(int size) {
thread_pool_min_threads=size;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setCorePoolSize(size);
return (T)this;
}
public int getThreadPoolMinThreads() {return thread_pool_min_threads;}
public T setThreadPoolMaxThreads(int size) {
thread_pool_max_threads=size;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size);
return (T)this;
}
public int getThreadPoolMaxThreads() {return thread_pool_max_threads;}
public T setThreadPoolKeepAliveTime(long time) {
thread_pool_keep_alive_time=time;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS);
return (T)this;
}
public long getThreadPoolKeepAliveTime() {return thread_pool_keep_alive_time;}
public Object[] getJmxObjects() {
return new Object[]{msg_stats, msg_processing_policy, bundler};
}
@Property(name="level", description="Sets the level")
public T setLevel(String level) {
T retval=super.setLevel(level);
is_trace=log.isTraceEnabled();
return retval;
}
@ManagedAttribute(description="Number of thread dumps")
public int getNumberOfThreadDumps() {return thread_dumps.get();}
@ManagedOperation(description="Resets the thread_dumps counter")
public void resetThreadDumps() {thread_dumps.set(0);}
@ManagedOperation(description="Changes the message processing policy. The fully qualified name of a class " +
"implementing MessageProcessingPolicy needs to be given")
public void setMessageProcessingPolicy(String policy) {
if(policy == null)
return;
if(policy.startsWith("submit")) {
msg_processing_policy=new SubmitToThreadPool();
msg_processing_policy.init(this);
return;
}
else if(policy.startsWith("max")) {
msg_processing_policy=new MaxOneThreadPerSender();
msg_processing_policy.init(this);
return;
}
try {
Class clazz=Util.loadClass(policy, getClass());
msg_processing_policy=clazz.getDeclaredConstructor().newInstance();
message_processing_policy=policy;
msg_processing_policy.init(this);
}
catch(Exception e) {
log.error("failed setting message_processing_policy", e);
}
}
/* --------------------------------------------- JMX ---------------------------------------------- */
protected final MsgStats msg_stats=new MsgStats();
/** The name of the group to which this member is connected. With a shared transport, the channel name is
* in TP.ProtocolAdapter (cluster_name), and this field is not used */
@ManagedAttribute(description="Channel (cluster) name")
protected AsciiString cluster_name;
@ManagedAttribute(description="If enabled, the timer will run non-blocking tasks on its own (runner) thread, and " +
"not submit them to the thread pool. Otherwise, all tasks are submitted to the thread pool. This attribute is " +
"experimental and may be removed without notice.")
protected boolean timer_handle_non_blocking_tasks=true;
@ManagedAttribute(description="Class of the timer implementation")
public String getTimerClass() {
return timer != null? timer.getClass().getSimpleName() : "null";
}
@ManagedAttribute(description="Name of the cluster to which this transport is connected")
public String getClusterName() {
return cluster_name != null? cluster_name.toString() : null;
}
public AsciiString getClusterNameAscii() {return cluster_name;}
@ManagedAttribute(description="Number of messages from members in a different cluster")
public int getDifferentClusterMessages() {
return suppress_log_different_cluster != null? suppress_log_different_cluster.getCache().size() : 0;
}
@ManagedAttribute(description="Number of messages from members with a different JGroups version")
public int getDifferentVersionMessages() {
return suppress_log_different_version != null? suppress_log_different_version.getCache().size() : 0;
}
@ManagedOperation(description="Clears the cache for messages from different clusters")
public T clearDifferentClusterCache() {
if(suppress_log_different_cluster != null)
suppress_log_different_cluster.getCache().clear();
return (T)this;
}
@ManagedOperation(description="Clears the cache for messages from members with different versions")
public T clearDifferentVersionCache() {
if(suppress_log_different_version != null)
suppress_log_different_version.getCache().clear();
return (T)this;
}
@ManagedAttribute(description="Type of logger used")
public static String loggerType() {return LogFactory.loggerType();}
/* --------------------------------------------- Fields ------------------------------------------------------ */
@ManagedOperation(description="If enabled, the timer will run non-blocking tasks on its own (runner) thread, and " +
"not submit them to the thread pool. Otherwise, all tasks are submitted to the thread pool. This attribute is " +
"experimental and may be removed without notice.")
public T enableBlockingTimerTasks(boolean flag) {
if(flag != this.timer_handle_non_blocking_tasks) {
this.timer_handle_non_blocking_tasks=flag;
timer.setNonBlockingTaskHandling(flag);
}
return (T)this;
}
/** The address (host and port) of this member */
protected Address local_addr;
protected PhysicalAddress local_physical_addr;
protected volatile View view;
/** The members of this group (updated when a member joins or leaves). With a shared transport,
* members contains *all* members from all channels sitting on the shared transport */
protected final Set members=new CopyOnWriteArraySet<>();
//http://jira.jboss.org/jira/browse/JGRP-849
protected final ReentrantLock connectLock = new ReentrantLock();
// ================================== Thread pool ======================
/** The thread pool which handles unmarshalling, version checks and dispatching of messages */
protected Executor thread_pool;
/** Factory which is used by the thread pool */
protected ThreadFactory thread_factory;
protected ThreadFactory internal_thread_factory;
protected Executor internal_pool; // only created if thread_pool is enabled, to handle internal msgs
// ================================== Timer thread pool =========================
protected TimeScheduler timer;
protected TimeService time_service;
// ================================= Default SocketFactory ========================
protected SocketFactory socket_factory=new DefaultSocketFactory();
protected Bundler bundler;
protected MessageProcessingPolicy msg_processing_policy=new MaxOneThreadPerSender();
protected DiagnosticsHandler diag_handler;
protected final List preregistered_probe_handlers=new LinkedList<>();
/** The header including the cluster name, sent with each message. Not used with a shared transport (instead
* TP.ProtocolAdapter attaches the header to the message */
protected TpHeader header;
/**
* Cache which maintains mappings between logical and physical addresses. When sending a message to a logical
* address, we look up the physical address from logical_addr_cache and send the message to the physical address
* The keys are logical addresses, the values physical addresses
*/
protected LazyRemovalCache logical_addr_cache;
// last time (in ns) we sent a discovery request
protected long last_discovery_request;
protected Future> logical_addr_cache_reaper;
protected final AverageMinMax avg_batch_size=new AverageMinMax();
protected static final LazyRemovalCache.Printable> print_function
=(logical_addr, entry) -> {
StringBuilder sb=new StringBuilder();
String tmp_logical_name=NameCache.get(logical_addr);
if(tmp_logical_name != null)
sb.append(tmp_logical_name).append(": ");
if(logical_addr instanceof UUID)
sb.append(((UUID)logical_addr).toStringLong()).append(": ");
sb.append(entry.toString(val -> val instanceof PhysicalAddress? val.printIpAddress() : val.toString()));
sb.append("\n");
return sb.toString();
};
/** Cache keeping track of WHO_HAS requests for physical addresses (given a logical address) and expiring
* them after who_has_cache_timeout ms */
protected ExpiryCache who_has_cache;
/** Log to suppress identical warnings for messages from members with different (incompatible) versions */
protected SuppressLog suppress_log_different_version;
/** Log to suppress identical warnings for messages from members in different clusters */
protected SuppressLog suppress_log_different_cluster;
/**
* Creates the TP protocol, and initializes the state variables, does
* however not start any sockets or threads.
*/
protected TP() {
}
public MsgStats getMessageStats() {return msg_stats;}
/** Whether or not hardware multicasting is supported */
public abstract boolean supportsMulticasting();
public boolean isMulticastCapable() {return supportsMulticasting();}
public String toString() {
return local_addr != null? getName() + "(local address: " + local_addr + ')' : getName();
}
@ManagedAttribute(description="The address of the channel")
public String getLocalAddress() {return local_addr != null? local_addr.toString() : null;}
public Address localAddress() {return local_addr;}
public View view() {return view;}
@ManagedAttribute(description="The physical address of the channel")
public String getLocalPhysicalAddress() {return local_physical_addr != null? local_physical_addr.printIpAddress() : null;}
public void resetStats() {
msg_stats.reset();
avg_batch_size.clear();
msg_processing_policy.reset();
}
public T registerProbeHandler(DiagnosticsHandler.ProbeHandler handler) {
if(diag_handler != null)
diag_handler.registerProbeHandler(handler);
else {
synchronized(preregistered_probe_handlers) {
preregistered_probe_handlers.add(handler);
}
}
return (T)this;
}
public T unregisterProbeHandler(DiagnosticsHandler.ProbeHandler handler) {
if(diag_handler != null)
diag_handler.unregisterProbeHandler(handler);
return (T)this;
}
public DiagnosticsHandler getDiagnosticsHandler() {return diag_handler;}
/**
* Sets a {@link DiagnosticsHandler}. Should be set before the stack is started
* @param handler
*/
public T setDiagnosticsHandler(DiagnosticsHandler handler) throws Exception {
if(handler != null) {
if(diag_handler != null)
diag_handler.stop();
diag_handler=handler;
if(diag_handler != null)
diag_handler.start();
}
return (T)this;
}
public Bundler getBundler() {return bundler;}
/** Installs a bundler. Needs to be done before the channel is connected */
public T setBundler(Bundler bundler) {
if(bundler != null)
this.bundler=bundler;
return (T)this;
}
public Executor getThreadPool() {
return thread_pool;
}
public T setThreadPool(Executor thread_pool) {
if(this.thread_pool != null)
shutdownThreadPool(this.thread_pool);
this.thread_pool=thread_pool;
if(timer instanceof TimeScheduler3)
((TimeScheduler3)timer).setThreadPool(thread_pool);
return (T)this;
}
public ThreadFactory getThreadPoolThreadFactory() {
return thread_factory;
}
public T setThreadPoolThreadFactory(ThreadFactory factory) {
thread_factory=factory;
if(thread_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)thread_pool).setThreadFactory(factory);
return (T)this;
}
public Executor getInternalThreadPool() {return internal_pool;}
public T setInternalThreadPool(Executor thread_pool) {
if(this.internal_pool != null)
shutdownThreadPool(this.internal_pool);
this.internal_pool=thread_pool;
return (T)this;
}
public ThreadFactory getInternalThreadPoolThreadFactory() {return internal_thread_factory;}
public T setInternalThreadPoolThreadFactory(ThreadFactory factory) {
internal_thread_factory=factory;
if(internal_pool instanceof ThreadPoolExecutor)
((ThreadPoolExecutor)internal_pool).setThreadFactory(factory);
return (T)this;
}
public TimeScheduler getTimer() {return timer;}
/**
* Sets a new timer. This should be done before the transport is initialized; be very careful, as replacing a
* running timer with tasks in it can wreak havoc !
* @param timer
*/
public T setTimer(TimeScheduler timer) {this.timer=timer; return (T)this;}
public TimeService getTimeService() {return time_service;}
public T setTimeService(TimeService ts) {
if(ts == null)
return (T)this;
if(time_service != null)
time_service.stop();
time_service=ts;
time_service.start();
return (T)this;
}
public ThreadFactory getThreadFactory() {
return thread_factory;
}
public T setThreadFactory(ThreadFactory factory) {thread_factory=factory; return (T)this;}
public SocketFactory getSocketFactory() {
return socket_factory;
}
@Override public void setSocketFactory(SocketFactory factory) {
if(factory != null)
socket_factory=factory;
}
/**
* Names the current thread. Valid values are "pcl":
* p: include the previous (original) name, e.g. "Incoming thread-1", "UDP ucast receiver"
* c: include the cluster name, e.g. "MyCluster"
* l: include the local address of the current member, e.g. "192.168.5.1:5678"
*/
public String getThreadNamingPattern() {return thread_naming_pattern;}
public long getNumMessagesSent() {return msg_stats.getNumMsgsSent();}
public T incrBatchesSent(int delta) {if(stats) msg_stats.incrNumBatchesSent(delta); return (T)this;}
public T incrNumSingleMsgsSent(int d) {if(stats) msg_stats.incrNumSingleMsgsSent(d); return (T)this;}
public InetAddress getBindAddress() {return bind_addr;}
public T setBindAddress(InetAddress a) {this.bind_addr=a; return (T)this;}
public int getBindPort() {return bind_port;}
public T setBindPort(int port) {this.bind_port=port; return (T)this;}
public T setBindToAllInterfaces(boolean f) {this.receive_on_all_interfaces=f; return (T)this;}
public boolean isReceiveOnAllInterfaces() {return receive_on_all_interfaces;}
public List getReceiveInterfaces() {return receive_interfaces;}
public T setPortRange(int range) {this.port_range=range; return (T)this;}
public int getPortRange() {return port_range;}
@ManagedAttribute(description="Current number of threads in the thread pool")
public int getThreadPoolSize() {
if(thread_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)thread_pool).getPoolSize();
return 0;
}
@ManagedAttribute(description="Current number of active threads in the thread pool")
public int getThreadPoolSizeActive() {
if(thread_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)thread_pool).getActiveCount();
return 0;
}
@ManagedAttribute(description="Largest number of threads in the thread pool")
public int getThreadPoolSizeLargest() {
if(thread_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)thread_pool).getLargestPoolSize();
return 0;
}
@ManagedAttribute(description="Current number of threads in the internal thread pool")
public int getInternalThreadPoolSize() {
if(internal_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)internal_pool).getPoolSize();
return 0;
}
@ManagedAttribute(description="Largest number of threads in the internal thread pool")
public int getInternalThreadPoolSizeLargest() {
if(internal_pool instanceof ThreadPoolExecutor)
return ((ThreadPoolExecutor)internal_pool).getLargestPoolSize();
return 0;
}
@ManagedAttribute(name="timer_tasks",description="Number of timer tasks queued up for execution")
public int getNumTimerTasks() {return timer.size();}
@ManagedOperation public String dumpTimerTasks() {return timer.dumpTimerTasks();}
@ManagedOperation(description="Purges cancelled tasks from the timer queue")
public void removeCancelledTimerTasks() {timer.removeCancelledTasks();}
@ManagedAttribute(description="Number of threads currently in the pool")
public int getTimerThreads() {return timer.getCurrentThreads();}
@ManagedAttribute(description="Returns the number of live threads in the JVM")
public static int getNumThreads() {return ManagementFactory.getThreadMXBean().getThreadCount();}
@ManagedAttribute(description="Whether the diagnostics handler is running or not")
public boolean isDiagnosticsRunning() {return diag_handler != null && diag_handler.isRunning();}
public T setLogDiscardMessages(boolean flag) {log_discard_msgs=flag; return (T)this;}
public boolean getLogDiscardMessages() {return log_discard_msgs;}
public T setLogDiscardMessagesVersion(boolean f) {log_discard_msgs_version=f; return (T)this;}
public boolean getLogDiscardMessagesVersion() {return log_discard_msgs_version;}
public boolean getUseIpAddresses() {return use_ip_addrs;}
public boolean isDiagnosticsEnabled() {return enable_diagnostics;}
public T setDiagnosticsEnabled(boolean f) {enable_diagnostics=f; return (T)this;}
public boolean isDiagUdEnabled() {return diag_handler != null && diag_handler.udpEnabled();}
public T diagEnableUdp(boolean f) {diag_enable_udp=f;
if(diag_handler != null) diag_handler.enableUdp(f); return (T)this;}
public boolean diagTcpEnabled() {return diag_enable_tcp;}
public T diagEnableTcp(boolean f) {diag_enable_tcp=f;
if(diag_handler != null) diag_handler.enableTcp(f); return (T)this;}
@ManagedOperation(description="Dumps the contents of the logical address cache")
public String printLogicalAddressCache() {
return logical_addr_cache.printCache(print_function);
}
@ManagedOperation(description="Prints the contents of the who-has cache")
public String printWhoHasCache() {return who_has_cache.toString();}
@ManagedOperation(description="Evicts elements in the logical address cache which have expired")
public void evictLogicalAddressCache() {
evictLogicalAddressCache(false);
}
public void evictLogicalAddressCache(boolean force) {
logical_addr_cache.removeMarkedElements(force);
fetchLocalAddresses();
}
/**
* Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N
* messages, one for each member
* @param data The data to be sent. This is not a copy, so don't modify it
* @param offset
* @param length
* @throws Exception
*/
public abstract void sendMulticast(byte[] data, int offset, int length) throws Exception;
/**
* Send a unicast to 1 member. Note that the destination address is a *physical*, not a logical address
* @param dest Must be a non-null unicast address
* @param data The data to be sent. This is not a copy, so don't modify it
* @param offset
* @param length
* @throws Exception
*/
public abstract void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception;
public abstract String getInfo();
/* ------------------------------------------------------------------------------- */
/*------------------------------ Protocol interface ------------------------------ */
public void init() throws Exception {
this.id=ClassConfigurator.getProtocolId(TP.class);
if(use_fibers) {
if(!Util.fibersAvailable()) {
log.warn("use_fibers was set to false, as fibers are not available in this Java version");
use_fibers=false;
}
}
if(thread_factory == null)
//thread_factory=new DefaultThreadFactory("jgroups", false, true);
thread_factory=new LazyThreadFactory("jgroups", false, true)
.useFibers(use_fibers).log(this.log);
if(internal_thread_factory == null)
internal_thread_factory=new LazyThreadFactory("jgroups-int", false, true)
.useFibers(use_fibers).log(this.log);
// local_addr is null when shared transport, channel_name is not used
setInAllThreadFactories(cluster_name != null? cluster_name.toString() : null, local_addr, thread_naming_pattern);
if(enable_diagnostics && diag_handler == null)
diag_handler=createDiagnosticsHandler();
who_has_cache=new ExpiryCache<>(who_has_cache_timeout);
if(suppress_time_different_version_warnings > 0)
suppress_log_different_version=new SuppressLog<>(log, "VersionMismatch", "SuppressMsg");
if(suppress_time_different_cluster_warnings > 0)
suppress_log_different_cluster=new SuppressLog<>(log, "MsgDroppedDiffCluster", "SuppressMsg");
// ====================================== Thread pool ===========================
if(use_common_fork_join_pool)
use_fork_join_pool=true;
if(use_fork_join_pool)
thread_pool_max_threads=Runtime.getRuntime().availableProcessors();
if(thread_pool == null || (thread_pool instanceof ExecutorService && ((ExecutorService)thread_pool).isShutdown())) {
if(thread_pool_enabled) {
int num_cores=Runtime.getRuntime().availableProcessors();
int max_internal_size=Math.max(4, num_cores);
log.debug("thread pool min/max/keep-alive: %d/%d/%d use_fork_join=%b, internal pool: %d/%d/%d (%d cores available)",
thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, use_fork_join_pool,
0, max_internal_size, 30000, num_cores);
thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time,
"abort", new SynchronousQueue<>(), thread_factory, log, use_fork_join_pool, use_common_fork_join_pool);
internal_pool=createThreadPool(0, max_internal_size, 30000, "abort",
new SynchronousQueue<>(), internal_thread_factory, log, false, false);
}
else // otherwise use the caller's thread to unmarshal the byte buffer into a message
thread_pool=new DirectExecutor();
}
// ========================================== Timer ==============================
if(timer == null) {
timer=new TimeScheduler3(thread_pool, thread_factory, false); // don't start the timer thread yet (JGRP-2332)
timer.setNonBlockingTaskHandling(timer_handle_non_blocking_tasks);
}
if(time_service_interval > 0)
time_service=new TimeService(timer, time_service_interval);
Map m=new HashMap<>(2);
if(bind_addr != null)
m.put("bind_addr", bind_addr);
if(external_addr != null)
m.put("external_addr", external_addr);
if(external_port > 0)
m.put("external_port", external_port);
if(!m.isEmpty())
up(new Event(Event.CONFIG, m));
logical_addr_cache=new LazyRemovalCache<>(logical_addr_cache_max_size, logical_addr_cache_expiration);
if(logical_addr_cache_reaper_interval > 0 && (logical_addr_cache_reaper == null || logical_addr_cache_reaper.isDone())) {
logical_addr_cache_reaper=timer.scheduleWithFixedDelay(new Runnable() {
public void run() {
evictLogicalAddressCache();
}
public String toString() {
return TP.this.getClass().getSimpleName() + ": LogicalAddressCacheReaper (interval=" + logical_addr_cache_reaper_interval + " ms)";
}
}, logical_addr_cache_reaper_interval, logical_addr_cache_reaper_interval, TimeUnit.MILLISECONDS, false);
}
if(message_processing_policy != null)
setMessageProcessingPolicy(message_processing_policy);
else
msg_processing_policy.init(this);
}
/** Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */
public void start() throws Exception {
timer.start();
if(time_service != null)
time_service.start();
if(use_ip_addrs) {
PhysicalAddress tmp=getPhysicalAddress();
if(tmp instanceof IpAddress) {
local_addr=new IpAddressUUID(((IpAddress)tmp).getIpAddress(), ((IpAddress)tmp).getPort());
stack.getTopProtocol().down(new Event(Event.SET_LOCAL_ADDRESS, local_addr));
stack.getTopProtocol().up(new Event(Event.SET_LOCAL_ADDRESS, local_addr));
}
}
fetchLocalAddresses();
startDiagnostics();
if(bundler == null)
bundler=createBundler(bundler_type);
bundler.init(this);
bundler.start();
// local_addr is null when shared transport
setInAllThreadFactories(cluster_name != null? cluster_name.toString() : null, local_addr, thread_naming_pattern);
}
public void stop() {
stopDiagnostics();
if(bundler != null) {
bundler.stop();
bundler=null;
}
if(msg_processing_policy != null)
msg_processing_policy.destroy();
if(time_service != null)
time_service.stop();
timer.stop();
}
public void destroy() {
super.destroy();
if(logical_addr_cache_reaper != null) {
logical_addr_cache_reaper.cancel(false);
logical_addr_cache_reaper=null;
}
// Stop the thread pool
if(thread_pool instanceof ExecutorService)
shutdownThreadPool(thread_pool);
if(internal_pool instanceof ExecutorService)
shutdownThreadPool(internal_pool);
}
@ManagedAttribute(description="Returns stats about the current bundler")
public String bundlerStats() {
Map tmp=bundler != null? bundler.getStats() : null;
return tmp != null? tmp.toString() : "n/a";
}
@ManagedOperation(description="Resets stats of the current bundler")
public void bundlerStatsReset() {bundler.resetStats();}
@ManagedOperation(description="Creates and sets a new bundler. Type has to be either a bundler_type or the fully " +
"qualified classname of a Bundler impl. Stops the current bundler (if running)")
public T bundler(String type) {
Bundler new_bundler=createBundler(type);
String old_bundler_class=null;
if(bundler != null) {
bundler.stop();
old_bundler_class=bundler.getClass().getName();
}
new_bundler.init(this);
new_bundler.start();
bundler=new_bundler;
bundler_type=type;
if(old_bundler_class != null)
log.debug("%s: replaced bundler %s with %s", local_addr, old_bundler_class, bundler.getClass().getName());
return (T)this;
}
@ManagedOperation(description="Enables diagnostics and starts DiagnosticsHandler (if not running)")
public void enableDiagnostics() {
enable_diagnostics=true;
try {
startDiagnostics();
}
catch(Exception e) {
log.error(Util.getMessage("FailedStartingDiagnostics"), e);
}
}
@ManagedOperation(description="Disables diagnostics and stops DiagnosticsHandler (if running)")
public void disableDiagnostics() {
enable_diagnostics=false;
stopDiagnostics();
}
protected void startDiagnostics() throws Exception {
if(enable_diagnostics) {
if(diag_handler == null)
diag_handler=createDiagnosticsHandler();
diag_handler.registerProbeHandler(this);
diag_handler.start();
synchronized(preregistered_probe_handlers) {
for(DiagnosticsHandler.ProbeHandler handler : preregistered_probe_handlers)
diag_handler.registerProbeHandler(handler);
}
}
synchronized(preregistered_probe_handlers) {
preregistered_probe_handlers.clear(); // https://issues.jboss.org/browse/JGRP-1834
}
}
protected void stopDiagnostics() {
if(diag_handler != null) {
diag_handler.unregisterProbeHandler(this);
diag_handler.stop();
}
synchronized(preregistered_probe_handlers) {
preregistered_probe_handlers.clear();
}
}
public Map handleProbe(String... keys) {
Map retval=new HashMap<>(keys != null? keys.length : 2);
if(keys == null)
return retval;
for(String key: keys) {
switch(key) {
case "dump":
retval.put(key, Util.dumpThreads());
break;
case "uuids":
retval.put(key, printLogicalAddressCache());
if(!retval.containsKey("local_addr"))
retval.put("local_addr", local_addr != null? local_addr.toString() : null);
break;
case "keys":
StringBuilder sb=new StringBuilder();
if(diag_handler != null) {
for(DiagnosticsHandler.ProbeHandler handler : diag_handler.getProbeHandlers()) {
String[] tmp=handler.supportedKeys();
if(tmp != null && tmp.length > 0) {
for(String s : tmp)
sb.append(s).append(" ");
}
}
}
retval.put(key, sb.toString());
break;
case "member-addrs":
Set physical_addrs=logical_addr_cache.nonRemovedValues();
String list=Util.print(physical_addrs);
retval.put(key, list);
break;
}
}
return retval;
}
public String[] supportedKeys() {
return new String[]{"dump", "keys", "uuids", "member-addrs"};
}
protected void handleConnect() throws Exception {
}
protected void handleDisconnect() {
}
public Object down(Event evt) {
switch(evt.getType()) {
case Event.TMP_VIEW:
case Event.VIEW_CHANGE:
Collection old_members;
synchronized(members) {
View v=evt.getArg();
this.view=v;
old_members=new ArrayList<>(members);
members.clear();
members.addAll(v.getMembers());
// fix for https://jira.jboss.org/jira/browse/JGRP-918
logical_addr_cache.retainAll(members);
fetchLocalAddresses();
List left_mbrs=Util.leftMembers(old_members,members);
if(left_mbrs != null && !left_mbrs.isEmpty())
NameCache.removeAll(left_mbrs);
if(suppress_log_different_version != null)
suppress_log_different_version.removeExpired(suppress_time_different_version_warnings);
if(suppress_log_different_cluster != null)
suppress_log_different_cluster.removeExpired(suppress_time_different_cluster_warnings);
}
who_has_cache.removeExpiredElements();
if(bundler != null)
bundler.viewChange(evt.getArg());
if(msg_processing_policy instanceof MaxOneThreadPerSender)
((MaxOneThreadPerSender)msg_processing_policy).viewChange(view.getMembers());
break;
case Event.CONNECT:
case Event.CONNECT_WITH_STATE_TRANSFER:
case Event.CONNECT_USE_FLUSH:
case Event.CONNECT_WITH_STATE_TRANSFER_USE_FLUSH:
cluster_name=new AsciiString((String)evt.getArg());
header=new TpHeader(cluster_name);
// local_addr is null when shared transport
setInAllThreadFactories(cluster_name != null? cluster_name.toString() : null, local_addr, thread_naming_pattern);
setThreadNames();
connectLock.lock();
try {
handleConnect();
}
catch(Exception e) {
throw new RuntimeException(e);
}
finally {
connectLock.unlock();
}
return null;
case Event.DISCONNECT:
unsetThreadNames();
connectLock.lock();
try {
handleDisconnect();
}
finally {
connectLock.unlock();
}
break;
case Event.GET_PHYSICAL_ADDRESS:
Address addr=evt.getArg();
PhysicalAddress physical_addr=getPhysicalAddressFromCache(addr);
if(physical_addr != null)
return physical_addr;
if(Objects.equals(addr, local_addr)) {
physical_addr=getPhysicalAddress();
if(physical_addr != null)
addPhysicalAddressToCache(addr, physical_addr);
}
return physical_addr;
case Event.GET_PHYSICAL_ADDRESSES:
return getAllPhysicalAddressesFromCache();
case Event.GET_LOGICAL_PHYSICAL_MAPPINGS:
Object arg=evt.getArg();
boolean skip_removed_values=arg instanceof Boolean && (Boolean)arg;
return logical_addr_cache.contents(skip_removed_values);
case Event.ADD_PHYSICAL_ADDRESS:
Tuple tuple=evt.getArg();
return addPhysicalAddressToCache(tuple.getVal1(), tuple.getVal2());
case Event.REMOVE_ADDRESS:
removeLogicalAddressFromCache(evt.getArg());
break;
case Event.SET_LOCAL_ADDRESS:
local_addr=evt.getArg();
registerLocalAddress(evt.getArg());
break;
}
return null;
}
/** A message needs to be sent to a single member or all members */
public Object down(Message msg) {
if(header != null)
msg.putHeader(this.id, header); // added patch by Roland Kurmann (March 20 2003)
setSourceAddress(msg); // very important !! listToBuffer() will fail with a null src address !!
Address dest=msg.getDest(), sender=msg.getSrc();
if(is_trace)
log.trace("%s: sending msg to %s, src=%s, size=%d, headers are %s", local_addr, dest, sender, msg.size(), msg.printHeaders());
// Don't send if dest is local address. Instead, send it up the stack. If multicast message, loop back directly
// to us (but still multicast). Once we receive this, we discard our own multicast message
boolean multicast=dest == null, do_send=multicast || !dest.equals(sender),
loop_back=(multicast || dest.equals(sender)) && !msg.isTransientFlagSet(Message.TransientFlag.DONT_LOOPBACK);
if(dest instanceof PhysicalAddress && dest.equals(local_physical_addr)) {
loop_back=true;
do_send=false;
}
if(loopback_separate_thread) {
if(loop_back)
loopback(msg, multicast);
if(do_send)
_send(msg, dest);
}
else {
if(do_send)
_send(msg, dest);
if(loop_back)
loopback(msg, multicast);
}
return null;
}
/*--------------------------- End of Protocol interface -------------------------- */
/* ------------------------------ Private Methods -------------------------------- */
protected DiagnosticsHandler createDiagnosticsHandler() {
return new DiagnosticsHandler(diagnostics_addr, diagnostics_port, diagnostics_bind_interfaces,
diagnostics_ttl, log, getSocketFactory(), getThreadFactory(), diagnostics_passcode)
.transport(this).setDiagnosticsBindAddress(diagnostics_bind_addr)
.enableUdp(diag_enable_udp).enableTcp(diag_enable_tcp)
.setDiagnosticsPortRange(diagnostics_port_range);
}
protected Bundler createBundler(String type) {
if(type == null)
throw new IllegalArgumentException("bundler type has to be non-null");
switch(type) {
case "transfer-queue":
case "tq":
return new TransferQueueBundler(bundler_capacity);
case "simplified-transfer-queue":
case "stq":
return new SimplifiedTransferQueueBundler(bundler_capacity);
case "sender-sends":
case "ss":
return new SenderSendsBundler();
case "ring-buffer":
case "rb":
return new RingBufferBundler(bundler_capacity).numSpins(bundler_num_spins).waitStrategy(bundler_wait_strategy);
case "ring-buffer-lockless":
case "rbl":
return new RingBufferBundlerLockless(bundler_capacity);
case "ring-buffer-lockless2":
case "rbl2":
return new RingBufferBundlerLockless2(bundler_capacity);
case "no-bundler":
case "nb":
return new NoBundler();
case "async-no-bundler":
case "anb":
return new AsyncNoBundler();
case "ab":
case "alternating-bundler":
return new AlternatingBundler();
case "rqb": case "rq":
case "remove-queue-bundler": case "remove-queue":
return new RemoveQueueBundler();
}
try {
Class clazz=Util.loadClass(type, getClass());
return clazz.getDeclaredConstructor().newInstance();
}
catch(Throwable t) {
log.warn("failed creating instance of bundler %s: %s", type, t);
}
return new TransferQueueBundler(bundler_capacity);
}
protected void loopback(Message msg, final boolean multicast) {
final Message copy=loopback_copy? msg.copy() : msg;
if(is_trace)
log.trace("%s: looping back message %s, headers are %s", local_addr, copy, copy.printHeaders());
if(!loopback_separate_thread) {
passMessageUp(copy, null, false, multicast, false);
return;
}
// changed to fix http://jira.jboss.com/jira/browse/JGRP-506
boolean internal=msg.isFlagSet(Message.Flag.INTERNAL);
boolean oob=msg.isFlagSet(Message.Flag.OOB);
msg_processing_policy.loopback(msg, oob, internal);
}
protected void _send(Message msg, Address dest) {
try {
send(msg);
}
catch(InterruptedIOException iex) {
}
catch(InterruptedException interruptedEx) {
Thread.currentThread().interrupt(); // let someone else handle the interrupt
}
catch(Throwable e) {
log.trace(Util.getMessage("SendFailure"),
local_addr, (dest == null? "cluster" : dest), msg.size(), e.toString(), msg.printHeaders());
}
}
/**
* If the sender is null, set our own address. We cannot just go ahead and set the address
* anyway, as we might be sending a message on behalf of someone else ! E.g. in case of
* retransmission, when the original sender has crashed, or in a FLUSH protocol when we
* have to return all unstable messages with the FLUSH_OK response.
*/
protected void setSourceAddress(Message msg) {
if(msg.getSrc() == null && local_addr != null) // should already be set by TP.ProtocolAdapter in shared transport case !
msg.setSrc(local_addr);
}
public void passMessageUp(Message msg, byte[] cluster_name, boolean perform_cluster_name_matching,
boolean multicast, boolean discard_own_mcast) {
if(is_trace)
log.trace("%s: received %s, headers are %s", local_addr, msg, msg.printHeaders());
if(up_prot == null)
return;
if(multicast && discard_own_mcast && local_addr != null && local_addr.equals(msg.getSrc()))
return;
// Discard if message's cluster name is not the same as our cluster name
if(perform_cluster_name_matching && this.cluster_name != null && !this.cluster_name.equals(cluster_name)) {
if(log_discard_msgs && log.isWarnEnabled()) {
Address sender=msg.getSrc();
if(suppress_log_different_cluster != null)
suppress_log_different_cluster.log(SuppressLog.Level.warn, sender,
suppress_time_different_cluster_warnings,
new AsciiString(cluster_name),this.cluster_name, sender);
else
log.warn(Util.getMessage("MsgDroppedDiffCluster"), new AsciiString(cluster_name),this.cluster_name, sender);
}
return;
}
up_prot.up(msg);
}
public void passBatchUp(MessageBatch batch, boolean perform_cluster_name_matching, boolean discard_own_mcast) {
if(is_trace)
log.trace("%s: received message batch of %d messages from %s", local_addr, batch.size(), batch.sender());
if(up_prot == null)
return;
// Discard if message's cluster name is not the same as our cluster name
if(perform_cluster_name_matching && cluster_name != null && !cluster_name.equals(batch.clusterName())) {
if(log_discard_msgs && log.isWarnEnabled()) {
Address sender=batch.sender();
if(suppress_log_different_cluster != null)
suppress_log_different_cluster.log(SuppressLog.Level.warn, sender,
suppress_time_different_cluster_warnings,
batch.clusterName(),cluster_name, sender);
else
log.warn(Util.getMessage("BatchDroppedDiffCluster"), batch.clusterName(),cluster_name, sender);
}
return;
}
if(batch.multicast() && discard_own_mcast && local_addr != null && local_addr.equals(batch.sender()))
return;
up_prot.up(batch);
}
/**
* Subclasses must call this method when a unicast or multicast message has been received.
*/
public void receive(Address sender, byte[] data, int offset, int length) {
if(data == null) return;
// drop message from self; it has already been looped back up (https://issues.jboss.org/browse/JGRP-1765)
if(Objects.equals(local_physical_addr, sender))
return;
// the length of a message needs to be at least 3 bytes: version (2) and flags (1) // JGRP-2210
if(length < Global.SHORT_SIZE + Global.BYTE_SIZE)
return;
short version=Bits.readShort(data, offset);
if(!versionMatch(version, sender))
return;
offset+=Global.SHORT_SIZE;
byte flags=data[offset];
offset+=Global.BYTE_SIZE;
boolean is_message_list=(flags & LIST) == LIST, multicast=(flags & MULTICAST) == MULTICAST;
ByteArrayDataInputStream in=new ByteArrayDataInputStream(data, offset, length);
if(is_message_list) // used if message bundling is enabled
handleMessageBatch(in, multicast);
else
handleSingleMessage(in, multicast);
}
public void receive(Address sender, DataInput in) throws Exception {
if(in == null) return;
// drop message from self; it has already been looped back up (https://issues.jboss.org/browse/JGRP-1765)
if(Objects.equals(local_physical_addr, sender))
return;
short version=in.readShort();
if(!versionMatch(version, sender))
return;
byte flags=in.readByte();
boolean is_message_list=(flags & LIST) == LIST, multicast=(flags & MULTICAST) == MULTICAST;
if(is_message_list) // used if message bundling is enabled
handleMessageBatch(in, multicast);
else
handleSingleMessage(in, multicast);
}
protected void handleMessageBatch(DataInput in, boolean multicast) {
try {
final MessageBatch[] batches=Util.readMessageBatch(in, multicast);
final MessageBatch batch=batches[0], oob_batch=batches[1], internal_batch_oob=batches[2], internal_batch=batches[3];
processBatch(oob_batch, true, false);
processBatch(batch, false, false);
processBatch(internal_batch_oob, true, true);
processBatch(internal_batch, false, true);
}
catch(Throwable t) {
log.error(String.format(Util.getMessage("IncomingMsgFailure"), local_addr), t);
}
}
protected void handleSingleMessage(DataInput in, boolean multicast) {
try {
Message msg=new Message(false); // don't create headers, readFrom() will do this
msg.readFrom(in);
if(!multicast && unicastDestMismatch(msg.getDest()))
return;
boolean oob=msg.isFlagSet(Message.Flag.OOB), internal=msg.isFlagSet(Message.Flag.INTERNAL);
msg_processing_policy.process(msg, oob, internal);
}
catch(Throwable t) {
log.error(String.format(Util.getMessage("IncomingMsgFailure"), local_addr), t);
}
}
protected void processBatch(MessageBatch batch, boolean oob, boolean internal) {
try {
if(batch != null && !batch.isEmpty() && !unicastDestMismatch(batch.getDest()))
msg_processing_policy.process(batch, oob, internal);
}
catch(Throwable t) {
log.error("processing batch failed", t);
}
}
public boolean unicastDestMismatch(Address dest) {
return dest != null && !(Objects.equals(dest, local_addr) || Objects.equals(dest, local_physical_addr));
}
public boolean submitToThreadPool(Runnable task, boolean spawn_thread_on_rejection) {
return submitToThreadPool(thread_pool, task, spawn_thread_on_rejection, true);
}
public boolean submitToThreadPool(Executor pool, Runnable task, boolean spawn_thread_on_rejection, boolean forward_to_internal_pool) {
try {
pool.execute(task);
}
catch(RejectedExecutionException ex) {
if(!spawn_thread_on_rejection) {
msg_stats.incrNumRejectedMsgs(1);
// https://issues.redhat.com/browse/JGRP-2403
if(thread_dumps.incrementAndGet() == thread_dumps_threshold) {
String thread_dump=Util.dumpThreads();
if(thread_dump_path != null) {
File f=new File(thread_dump_path, "jgroups_threaddump_" + System.currentTimeMillis() + ".txt");
try(BufferedWriter writer=new BufferedWriter(new FileWriter(f))) {
writer.write(thread_dump);
log.fatal("%s: thread pool is full (max=%d, active=%d); thread dump (dumped once, until thread_dump is reset): %s",
local_addr, thread_pool_max_threads, getThreadPoolSize(), f.getAbsolutePath());
}
catch(IOException e) {
log.warn("%s: cannot generate the thread dump to %s: %s", local_addr, f.getAbsolutePath(), e);
log.fatal("%s: thread pool is full (max=%d, active=%d); " +
"thread dump (dumped once, until thread_dump is reset):\n%s",
local_addr, thread_pool_max_threads, getThreadPoolSize(), thread_dump);
}
}
else
log.fatal("%s: thread pool is full (max=%d, active=%d); thread dump (dumped once, until thread_dump is reset):\n%s",
local_addr, thread_pool_max_threads, getThreadPoolSize(), thread_dump);
}
return false;
}
if(forward_to_internal_pool && internal_pool != null)
return submitToThreadPool(internal_pool, task, true, false);
else {
if(spawn_thread_on_full_pool) {
msg_stats.incrNumThreadsSpawned(1);
return runInNewThread(task);
}
msg_stats.incrNumRejectedMsgs(1);
return false;
}
}
catch(Throwable t) {
log.error("failure submitting task to thread pool", t);
msg_stats.incrNumRejectedMsgs(1);
return false;
}
return true;
}
protected boolean runInNewThread(Runnable task) {
try {
Thread thread=thread_factory != null? thread_factory.newThread(task, "jgroups-temp-thread")
: new Thread(task, "jgroups-temp-thread");
thread.start();
return true;
}
catch(Throwable t) {
log.error("failed spawning new thread", t);
return false;
}
}
protected boolean versionMatch(short version, Address sender) {
boolean match=Version.isBinaryCompatible(version);
if(!match && log_discard_msgs_version && log.isWarnEnabled()) {
if(suppress_log_different_version != null)
suppress_log_different_version.log(SuppressLog.Level.warn, sender,
suppress_time_different_version_warnings,
sender, Version.print(version), Version.printVersion());
else
log.warn(Util.getMessage("VersionMismatch"), sender, Version.print(version), Version.printVersion());
}
return match;
}
/** Serializes and sends a message. This method is not reentrant */
protected void send(Message msg) throws Exception {
// bundle all messages, even the ones tagged with DONT_BUNDLE: https://issues.jboss.org/browse/JGRP-1737
// remove the ones tagged as OOB|DONT_BUNDLE at the receiver and pass them up individually (in separate threads)
Bundler tmp_bundler=bundler;
if(tmp_bundler != null)
tmp_bundler.send(msg);
}
public void doSend(byte[] buf, int offset, int length, Address dest) throws Exception {
if(stats) {
msg_stats.incrNumMsgsSent(1);
msg_stats.incrNumBytesSent(length);
}
if(dest == null)
sendMulticast(buf, offset, length);
else
sendToSingleMember(dest, buf, offset, length);
}
protected void sendToSingleMember(final Address dest, byte[] buf, int offset, int length) throws Exception {
if(dest instanceof PhysicalAddress) {
sendUnicast((PhysicalAddress)dest, buf, offset, length);
return;
}
PhysicalAddress physical_dest;
if((physical_dest=getPhysicalAddressFromCache(dest)) != null) {
sendUnicast(physical_dest,buf,offset,length);
return;
}
if(who_has_cache.addIfAbsentOrExpired(dest)) { // true if address was added
// FIND_MBRS must return quickly
Responses responses=fetchResponsesFromDiscoveryProtocol(Collections.singletonList(dest));
try {
for(PingData data : responses) {
if(data.getAddress() != null && data.getAddress().equals(dest)) {
if((physical_dest=data.getPhysicalAddr()) != null) {
sendUnicast(physical_dest, buf, offset, length);
return;
}
}
}
log.warn(Util.getMessage("PhysicalAddrMissing"), local_addr, dest);
}
finally {
responses.done();
}
}
}
/** Fetches the physical addrs for mbrs and sends the msg to each physical address. Asks discovery for missing
* members' physical addresses if needed */
protected void sendToMembers(Collection mbrs, byte[] buf, int offset, int length) throws Exception {
List missing=null;
if(mbrs == null || mbrs.isEmpty())
mbrs=logical_addr_cache.keySet();
for(Address mbr: mbrs) {
PhysicalAddress target=mbr instanceof PhysicalAddress? (PhysicalAddress)mbr : logical_addr_cache.get(mbr);
if(target == null) {
if(missing == null)
missing=new ArrayList<>(mbrs.size());
missing.add(mbr);
continue;
}
try {
if(!Objects.equals(local_physical_addr, target))
sendUnicast(target, buf, offset, length);
}
catch(SocketException | SocketTimeoutException sock_ex) {
log.debug(Util.getMessage("FailureSendingToPhysAddr"), local_addr, mbr, sock_ex);
}
catch(Throwable t) {
log.error(Util.getMessage("FailureSendingToPhysAddr"), local_addr, mbr, t);
}
}
if(missing != null)
fetchPhysicalAddrs(missing);
}
protected void fetchPhysicalAddrs(List missing) {
long current_time=0;
boolean do_send=false;
synchronized(this) {
if(last_discovery_request == 0 ||
(current_time=timestamp()) - last_discovery_request >= MIN_WAIT_BETWEEN_DISCOVERIES) {
last_discovery_request=current_time == 0? timestamp() : current_time;
do_send=true;
}
}
if(do_send) {
missing.removeAll(logical_addr_cache.keySet());
if(!missing.isEmpty()) { // FIND_MBRS either returns immediately or is processed in a separate thread
Responses rsps=fetchResponsesFromDiscoveryProtocol(missing);
rsps.done();
}
}
}
protected Responses fetchResponsesFromDiscoveryProtocol(List missing) {
return (Responses)up_prot.up(new Event(Event.FIND_MBRS, missing));
}
protected long timestamp() {return time_service != null? time_service.timestamp() : System.nanoTime();}
/**
* Associates the address with the physical address fetched from the cache
* @param addr
*/
protected void registerLocalAddress(Address addr) {
PhysicalAddress physical_addr=getPhysicalAddress();
if(physical_addr == null)
return;
local_physical_addr=physical_addr;
if(addr != null) {
if(use_ip_addrs && local_addr instanceof IpAddressUUID)
addPhysicalAddressToCache(addr, (PhysicalAddress)local_addr, true);
else
addPhysicalAddressToCache(addr, physical_addr, true);
}
}
/**
* Grabs the local address (or addresses in the shared transport case) and registers them with the physical address
* in the transport's cache
*/
protected void fetchLocalAddresses() {
if(local_addr != null)
registerLocalAddress(local_addr);
else {
Address addr=(Address)up_prot.up(new Event(Event.GET_LOCAL_ADDRESS));
local_addr=addr;
registerLocalAddress(addr);
}
}
protected void setThreadNames() {
if(diag_handler != null)
diag_handler.setThreadNames();
if(bundler instanceof TransferQueueBundler)
thread_factory.renameThread(TransferQueueBundler.THREAD_NAME, ((TransferQueueBundler)bundler).getThread());
}
protected void unsetThreadNames() {
if(diag_handler != null)
diag_handler.unsetThreadNames();
if(bundler instanceof TransferQueueBundler) {
Thread thread=((TransferQueueBundler)bundler).getThread();
if(thread != null)
thread_factory.renameThread(TransferQueueBundler.THREAD_NAME, thread);
}
else if(bundler instanceof RingBufferBundler) {
Thread thread=((RingBufferBundler)bundler).getThread();
if(thread != null)
thread_factory.renameThread(RingBufferBundler.THREAD_NAME, thread);
}
}
protected void setInAllThreadFactories(String cluster_name, Address local_address, String pattern) {
ThreadFactory[] factories= {thread_factory,internal_thread_factory};
for(ThreadFactory factory: factories) {
if(pattern != null)
factory.setPattern(pattern);
if(cluster_name != null) // if we have a shared transport, use singleton_name as cluster_name
factory.setClusterName(cluster_name);
if(local_address != null)
factory.setAddress(local_address.toString());
}
}
protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, String rejection_policy,
BlockingQueue queue, final ThreadFactory factory, Log log,
boolean use_fork_join_pool, boolean use_common_fork_join_pool) {
if(use_fork_join_pool) {
if(use_common_fork_join_pool)
return ForkJoinPool.commonPool();
int num_cores=Runtime.getRuntime().availableProcessors();
if(max_threads > num_cores)
log.warn("max_threads (%d) is higher than available cores (%d)", max_threads, num_cores);
return new ForkJoinPool(max_threads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
}
ThreadPoolExecutor pool=new ThreadPoolExecutor(min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, queue, factory);
RejectedExecutionHandler handler=Util.parseRejectionPolicy(rejection_policy);
pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler));
return pool;
}
protected static void shutdownThreadPool(Executor thread_pool) {
if(thread_pool instanceof ExecutorService) {
ExecutorService service=(ExecutorService)thread_pool;
service.shutdownNow();
try {
service.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS);
}
catch(InterruptedException e) {
}
}
}
protected boolean addPhysicalAddressToCache(Address logical_addr, PhysicalAddress physical_addr) {
return addPhysicalAddressToCache(logical_addr, physical_addr, true);
}
protected boolean addPhysicalAddressToCache(Address logical_addr, PhysicalAddress physical_addr, boolean overwrite) {
return logical_addr != null && physical_addr != null &&
overwrite? logical_addr_cache.add(logical_addr, physical_addr) : logical_addr_cache.addIfAbsent(logical_addr, physical_addr);
}
protected PhysicalAddress getPhysicalAddressFromCache(Address logical_addr) {
return logical_addr != null? logical_addr_cache.get(logical_addr) : null;
}
protected Collection getAllPhysicalAddressesFromCache() {
return logical_addr_cache.nonRemovedValues();
}
protected void removeLogicalAddressFromCache(Address logical_addr) {
if(logical_addr != null) {
logical_addr_cache.remove(logical_addr);
fetchLocalAddresses();
}
}
/** Clears the cache. Do not use, this is only for unit testing ! */
@ManagedOperation(description="Clears the logical address cache; only used for testing")
public void clearLogicalAddressCache() {
logical_addr_cache.clear(true);
fetchLocalAddresses();
}
protected abstract PhysicalAddress getPhysicalAddress();
/* ----------------------------- End of Private Methods ---------------------------------------- */
}