Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.rabbitmq.client.impl.recovery.AutorecoveringConnection Maven / Gradle / Ivy
package com.rabbitmq.client.impl.recovery;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.BlockedListener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Recoverable;
import com.rabbitmq.client.RecoveryListener;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.TopologyRecoveryException;
import com.rabbitmq.client.impl.ConnectionParams;
import com.rabbitmq.client.ExceptionHandler;
import com.rabbitmq.client.impl.FrameHandlerFactory;
import com.rabbitmq.client.impl.NetworkConnection;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Connection implementation that performs automatic recovery when
* connection shutdown is not initiated by the application (e.g. due to
* an I/O exception).
*
* Topology (exchanges, queues, bindings, and consumers) can be (and by default is) recovered
* as well, in this order:
*
*
* Exchanges
* Queues
* Bindings (both queue and exchange-to-exchange)
* Consumers
*
*
* @see com.rabbitmq.client.Connection
* @see com.rabbitmq.client.Recoverable
* @see com.rabbitmq.client.ConnectionFactory#setAutomaticRecoveryEnabled(boolean)
* @see com.rabbitmq.client.ConnectionFactory#setTopologyRecoveryEnabled(boolean)
* @since 3.3.0
*/
public class AutorecoveringConnection implements Connection, Recoverable, NetworkConnection {
private final RecoveryAwareAMQConnectionFactory cf;
private final Map channels;
private final ConnectionParams params;
private RecoveryAwareAMQConnection delegate;
private final List shutdownHooks = new ArrayList();
private final List recoveryListeners = new ArrayList();
private final List blockedListeners = new ArrayList();
// Records topology changes
private final Map recordedQueues = new ConcurrentHashMap();
private final List recordedBindings = new ArrayList();
private Map recordedExchanges = new ConcurrentHashMap();
private final Map consumers = new ConcurrentHashMap();
public AutorecoveringConnection(ConnectionParams params, FrameHandlerFactory f, Address[] addrs) {
this.cf = new RecoveryAwareAMQConnectionFactory(params, f, addrs);
this.params = params;
this.channels = new ConcurrentHashMap();
}
/**
* Private API.
* @throws IOException
* @see com.rabbitmq.client.ConnectionFactory#newConnection(java.util.concurrent.ExecutorService)
*/
public void init() throws IOException {
this.delegate = this.cf.newConnection();
this.addAutomaticRecoveryListener();
}
public void start() throws IOException {
// no-op, AMQConnection#start is executed in ConnectionFactory#newConnection
// and invoking it again will result in a framing error. MK.
}
/**
* @see com.rabbitmq.client.Connection#createChannel()
*/
public Channel createChannel() throws IOException {
RecoveryAwareChannelN ch = (RecoveryAwareChannelN) delegate.createChannel();
if (ch == null) {
return null;
} else {
return this.wrapChannel(ch);
}
}
/**
* @see com.rabbitmq.client.Connection#createChannel(int)
*/
public Channel createChannel(int channelNumber) throws IOException {
return delegate.createChannel(channelNumber);
}
/**
* Creates a recovering channel from a regular channel and registers it.
* If the regular channel cannot be created (e.g. too many channels are open
* already), returns null.
*
* @param delegateChannel Channel to wrap.
* @return Recovering channel.
*/
private Channel wrapChannel(RecoveryAwareChannelN delegateChannel) {
final AutorecoveringChannel channel = new AutorecoveringChannel(this, delegateChannel);
if (delegateChannel == null) {
return null;
} else {
this.registerChannel(channel);
return channel;
}
}
void registerChannel(AutorecoveringChannel channel) {
this.channels.put(channel.getChannelNumber(), channel);
}
void unregisterChannel(AutorecoveringChannel channel) {
this.channels.remove(channel.getChannelNumber());
}
/**
* @see com.rabbitmq.client.Connection#getServerProperties()
*/
public Map getServerProperties() {
return delegate.getServerProperties();
}
/**
* @see com.rabbitmq.client.Connection#getClientProperties()
*/
public Map getClientProperties() {
return delegate.getClientProperties();
}
/**
* @see com.rabbitmq.client.Connection#getFrameMax()
*/
public int getFrameMax() {
return delegate.getFrameMax();
}
/**
* @see com.rabbitmq.client.Connection#getHeartbeat()
*/
public int getHeartbeat() {
return delegate.getHeartbeat();
}
/**
* @see com.rabbitmq.client.Connection#getChannelMax()
*/
public int getChannelMax() {
return delegate.getChannelMax();
}
/**
* @see com.rabbitmq.client.Connection#isOpen()
*/
public boolean isOpen() {
return delegate.isOpen();
}
/**
* @see com.rabbitmq.client.Connection#close()
*/
public void close() throws IOException {
delegate.close();
}
/**
* @see Connection#close(int)
*/
public void close(int timeout) throws IOException {
delegate.close(timeout);
}
/**
* @see Connection#close(int, String, int)
*/
public void close(int closeCode, String closeMessage, int timeout) throws IOException {
delegate.close(closeCode, closeMessage, timeout);
}
/**
* @see com.rabbitmq.client.Connection#abort()
*/
public void abort() {
delegate.abort();
}
/**
* @see Connection#abort(int, String, int)
*/
public void abort(int closeCode, String closeMessage, int timeout) {
delegate.abort(closeCode, closeMessage, timeout);
}
/**
* @see Connection#abort(int, String)
*/
public void abort(int closeCode, String closeMessage) {
delegate.abort(closeCode, closeMessage);
}
/**
* @see Connection#abort(int)
*/
public void abort(int timeout) {
delegate.abort(timeout);
}
/**
* @see com.rabbitmq.client.Connection#getCloseReason()
*/
public ShutdownSignalException getCloseReason() {
return delegate.getCloseReason();
}
/**
* @see com.rabbitmq.client.ShutdownNotifier#addShutdownListener(com.rabbitmq.client.ShutdownListener)
*/
public void addBlockedListener(BlockedListener listener) {
this.blockedListeners.add(listener);
delegate.addBlockedListener(listener);
}
/**
* @see Connection#removeBlockedListener(com.rabbitmq.client.BlockedListener)
*/
public boolean removeBlockedListener(BlockedListener listener) {
this.blockedListeners.remove(listener);
return delegate.removeBlockedListener(listener);
}
/**
* @see com.rabbitmq.client.Connection#clearBlockedListeners()
*/
public void clearBlockedListeners() {
this.blockedListeners.clear();
delegate.clearBlockedListeners();
}
/**
* @see com.rabbitmq.client.Connection#close(int, String)
*/
public void close(int closeCode, String closeMessage) throws IOException {
delegate.close(closeCode, closeMessage);
}
/**
* @see Connection#addShutdownListener(com.rabbitmq.client.ShutdownListener)
*/
public void addShutdownListener(ShutdownListener listener) {
this.shutdownHooks.add(listener);
delegate.addShutdownListener(listener);
}
/**
* @see com.rabbitmq.client.ShutdownNotifier#removeShutdownListener(com.rabbitmq.client.ShutdownListener)
*/
public void removeShutdownListener(ShutdownListener listener) {
this.shutdownHooks.remove(listener);
delegate.removeShutdownListener(listener);
}
/**
* @see com.rabbitmq.client.ShutdownNotifier#notifyListeners()
*/
public void notifyListeners() {
delegate.notifyListeners();
}
/**
* Adds the recovery listener
* @param listener {@link com.rabbitmq.client.RecoveryListener} to execute after this connection recovers from network failure
*/
public void addRecoveryListener(RecoveryListener listener) {
this.recoveryListeners.add(listener);
}
/**
* Removes the recovery listener
* @param listener {@link com.rabbitmq.client.RecoveryListener} to remove
*/
public void removeRecoveryListener(RecoveryListener listener) {
this.recoveryListeners.remove(listener);
}
/**
* @see com.rabbitmq.client.impl.AMQConnection#getExceptionHandler()
*/
@SuppressWarnings("unused")
public ExceptionHandler getExceptionHandler() {
return this.delegate.getExceptionHandler();
}
/**
* @see com.rabbitmq.client.Connection#getPort()
*/
public int getPort() {
return delegate.getPort();
}
/**
* @see com.rabbitmq.client.Connection#getAddress()
*/
public InetAddress getAddress() {
return delegate.getAddress();
}
/**
* @return client socket address
*/
public InetAddress getLocalAddress() {
return this.delegate.getLocalAddress();
}
/**
* @return client socket port
*/
public int getLocalPort() {
return this.delegate.getLocalPort();
}
//
// Recovery
//
private void addAutomaticRecoveryListener() {
final AutorecoveringConnection c = this;
ShutdownListener automaticRecoveryListener = new ShutdownListener() {
public void shutdownCompleted(ShutdownSignalException cause) {
try {
if (!cause.isInitiatedByApplication()) {
c.beginAutomaticRecovery();
}
} catch (Exception e) {
c.delegate.getExceptionHandler().handleConnectionRecoveryException(c, e);
}
}
};
synchronized (this) {
this.shutdownHooks.add(automaticRecoveryListener);
this.delegate.addShutdownListener(automaticRecoveryListener);
}
}
synchronized private void beginAutomaticRecovery() throws InterruptedException, IOException, TopologyRecoveryException {
Thread.sleep(this.params.getNetworkRecoveryInterval());
this.recoverConnection();
this.recoverShutdownListeners();
this.recoverBlockedListeners();
this.recoverChannels();
if(this.params.isTopologyRecoveryEnabled()) {
this.recoverEntities();
this.recoverConsumers();
}
this.notifyRecoveryListeners();
}
private void recoverShutdownListeners() {
for (ShutdownListener sh : this.shutdownHooks) {
this.delegate.addShutdownListener(sh);
}
}
private void recoverBlockedListeners() {
for (BlockedListener bl : this.blockedListeners) {
this.delegate.addBlockedListener(bl);
}
}
private void recoverConnection() throws IOException, InterruptedException {
boolean recovering = true;
while (recovering) {
try {
this.delegate = this.cf.newConnection();
recovering = false;
} catch (Exception e) {
// TODO: exponential back-off
Thread.sleep(this.params.getNetworkRecoveryInterval());
this.getExceptionHandler().handleConnectionRecoveryException(this, e);
}
}
}
private void recoverChannels() {
for (AutorecoveringChannel ch : this.channels.values()) {
try {
ch.automaticallyRecover(this, this.delegate);
} catch (Throwable t) {
this.delegate.getExceptionHandler().handleChannelRecoveryException(ch, t);
}
}
}
private void notifyRecoveryListeners() {
for (RecoveryListener f : this.recoveryListeners) {
f.handleRecovery(this);
}
}
private void recoverEntities() throws TopologyRecoveryException {
// The recovery sequence is the following:
//
// 1. Recover exchanges
// 2. Recover queues
// 3. Recover bindings
// 4. Recover consumers
recoverExchanges();
recoverQueues();
recoverBindings();
}
private void recoverExchanges() {
// recorded exchanges are guaranteed to be
// non-predefined (we filter out predefined ones
// in exchangeDeclare). MK.
for (RecordedExchange x : this.recordedExchanges.values()) {
try {
x.recover();
} catch (Exception cause) {
final String message = "Caught an exception while recovering exchange " + x.getName() +
": " + cause.getMessage();
TopologyRecoveryException e = new TopologyRecoveryException(message, cause);
this.getExceptionHandler().handleTopologyRecoveryException(delegate, x.getDelegateChannel(), e);
}
}
}
private void recoverQueues() {
Map copy = new HashMap(this.recordedQueues);
for (Map.Entry entry : copy.entrySet()) {
String oldName = entry.getKey();
RecordedQueue q = entry.getValue();
try {
q.recover();
String newName = q.getName();
// make sure server-named queues are re-added with
// their new names. MK.
synchronized (this.recordedQueues) {
deleteRecordedQueue(oldName);
this.recordedQueues.put(newName, q);
this.propagateQueueNameChangeToBindings(oldName, newName);
this.propagateQueueNameChangeToConsumers(oldName, newName);
}
} catch (Exception cause) {
final String message = "Caught an exception while recovering queue " + oldName +
": " + cause.getMessage();
TopologyRecoveryException e = new TopologyRecoveryException(message, cause);
this.getExceptionHandler().handleTopologyRecoveryException(delegate, q.getDelegateChannel(), e);
}
}
}
private void recoverBindings() {
for (RecordedBinding b : this.recordedBindings) {
try {
b.recover();
} catch (Exception cause) {
String message = "Caught an exception while recovering binding between " + b.getSource() +
" and " + b.getDestination() + ": " + cause.getMessage();
TopologyRecoveryException e = new TopologyRecoveryException(message, cause);
this.getExceptionHandler().handleTopologyRecoveryException(delegate, b.getDelegateChannel(), e);
}
}
}
private void recoverConsumers() {
Map copy = new HashMap(this.consumers);
for (Map.Entry entry : copy.entrySet()) {
String tag = entry.getKey();
RecordedConsumer consumer = entry.getValue();
try {
String newTag = consumer.recover();
// make sure server-generated tags are re-added. MK.
synchronized (this.consumers) {
this.consumers.remove(tag);
this.consumers.put(newTag, consumer);
}
} catch (Exception cause) {
final String message = "Caught an exception while recovering consumer " + tag +
": " + cause.getMessage();
TopologyRecoveryException e = new TopologyRecoveryException(message, cause);
this.getExceptionHandler().handleTopologyRecoveryException(delegate, consumer.getDelegateChannel(), e);
}
}
}
private void propagateQueueNameChangeToBindings(String oldName, String newName) {
for (RecordedBinding b : this.recordedBindings) {
if (b.getDestination().equals(oldName)) {
b.setDestination(newName);
}
}
}
private void propagateQueueNameChangeToConsumers(String oldName, String newName) {
for (RecordedConsumer c : this.consumers.values()) {
if (c.getQueue().equals(oldName)) {
c.setQueue(newName);
}
}
}
synchronized void recordQueueBinding(AutorecoveringChannel ch,
String queue,
String exchange,
String routingKey,
Map arguments) {
RecordedBinding binding = new RecordedQueueBinding(ch).
source(exchange).
destination(queue).
routingKey(routingKey).
arguments(arguments);
if (!this.recordedBindings.contains(binding)) {
this.recordedBindings.add(binding);
}
}
synchronized boolean deleteRecordedQueueBinding(AutorecoveringChannel ch,
String queue,
String exchange,
String routingKey,
Map arguments) {
RecordedBinding b = new RecordedQueueBinding(ch).
source(exchange).
destination(queue).
routingKey(routingKey).
arguments(arguments);
return this.recordedBindings.remove(b);
}
synchronized void recordExchangeBinding(AutorecoveringChannel ch,
String destination,
String source,
String routingKey,
Map arguments) {
RecordedBinding binding = new RecordedExchangeBinding(ch).
source(source).
destination(destination).
routingKey(routingKey).
arguments(arguments);
this.recordedBindings.add(binding);
}
synchronized boolean deleteRecordedExchangeBinding(AutorecoveringChannel ch,
String destination,
String source,
String routingKey,
Map arguments) {
RecordedBinding b = new RecordedExchangeBinding(ch).
source(source).
destination(destination).
routingKey(routingKey).
arguments(arguments);
return this.recordedBindings.remove(b);
}
void recordQueue(AMQP.Queue.DeclareOk ok, RecordedQueue q) {
this.recordedQueues.put(ok.getQueue(), q);
}
void deleteRecordedQueue(String queue) {
this.recordedQueues.remove(queue);
}
void recordExchange(String exchange, RecordedExchange x) {
this.recordedExchanges.put(exchange, x);
}
void deleteRecordedExchange(String exchange) {
this.recordedExchanges.remove(exchange);
}
void recordConsumer(String result, RecordedConsumer consumer) {
this.consumers.put(result, consumer);
}
void deleteRecordedConsumer(String consumerTag) {
this.consumers.remove(consumerTag);
}
}