Java tutorial
//The contents of this file are subject to the Mozilla Public License //Version 1.1 (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.mozilla.org/MPL/ // //Software distributed under the License is distributed on an "AS IS" //basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See //the License for the specific language governing rights and //limitations under the License. // //The Original Code is RabbitMQ. // //The Initial Developer of the Original Code is GoPivotal, Inc. //Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. // package de.htwk_leipzig.bis.connection.handshake.clientRewrite; import java.io.EOFException; import java.io.IOException; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeoutException; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.AlreadyClosedException; import com.rabbitmq.client.AuthenticationFailureException; import com.rabbitmq.client.BlockedListener; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Command; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ExceptionHandler; import com.rabbitmq.client.LongString; import com.rabbitmq.client.Method; import com.rabbitmq.client.MissedHeartbeatException; import com.rabbitmq.client.PossibleAuthenticationFailureException; import com.rabbitmq.client.ProtocolVersionMismatchException; import com.rabbitmq.client.SaslConfig; import com.rabbitmq.client.SaslMechanism; import com.rabbitmq.client.ShutdownSignalException; import com.rabbitmq.client.impl.ChannelManager; import com.rabbitmq.client.impl.ChannelN; import com.rabbitmq.client.impl.ClientVersion; import com.rabbitmq.client.impl.ConnectionParams; import com.rabbitmq.client.impl.ConsumerWorkService; import com.rabbitmq.client.impl.Frame; import com.rabbitmq.client.impl.FrameHandler; import com.rabbitmq.client.impl.LongStringHelper; import com.rabbitmq.client.impl.NetworkConnection; import com.rabbitmq.client.impl.ShutdownNotifierComponent; import com.rabbitmq.client.impl.Version; import com.rabbitmq.utility.BlockingCell; import de.htwk_leipzig.bis.connection.handshake.HandshakeAction; import de.htwk_leipzig.bis.connection.handshake.clientRewrite.AMQChannel.BlockingRpcContinuation; final class Copyright { final static String COPYRIGHT = "Copyright (C) 2007-2014 GoPivotal, Inc."; final static String LICENSE = "Licensed under the MPL. See http://www.rabbitmq.com/"; } /** * Concrete class representing and managing an AMQP connection to a broker. * <p> * To create a broker connection, use {@link ConnectionFactory}. See * {@link Connection} for an example. */ public class AMQConnection extends ShutdownNotifierComponent implements Connection, NetworkConnection { /* * Change handshake Timout to max value */ public static final int HANDSHAKE_TIMEOUT = 30000; private final ExecutorService executor; private Thread mainLoopThread; private ThreadFactory threadFactory = Executors.defaultThreadFactory(); private HandshakeAction mHandshakeAction = new HandshakeAction() { @Override public void doAction() { } }; public HandshakeAction getHandshakeAction() { return mHandshakeAction; } public void setHandshakeAction(HandshakeAction handshakeAction) { mHandshakeAction = handshakeAction; } /** * Retrieve a copy of the default table of client properties that will be * sent to the server during connection startup. This method is called when * each new ConnectionFactory instance is constructed. * * @return a map of client properties * @see Connection#getClientProperties */ public static final Map<String, Object> defaultClientProperties() { Map<String, Object> props = new HashMap<String, Object>(); props.put("product", LongStringHelper.asLongString("RabbitMQ")); props.put("version", LongStringHelper.asLongString(ClientVersion.VERSION)); props.put("platform", LongStringHelper.asLongString("Java")); props.put("copyright", LongStringHelper.asLongString(Copyright.COPYRIGHT)); props.put("information", LongStringHelper.asLongString(Copyright.LICENSE)); Map<String, Object> capabilities = new HashMap<String, Object>(); capabilities.put("publisher_confirms", true); capabilities.put("exchange_exchange_bindings", true); capabilities.put("basic.nack", true); capabilities.put("consumer_cancel_notify", true); capabilities.put("connection.blocked", true); capabilities.put("authentication_failure_close", true); props.put("capabilities", capabilities); return props; } private static final Version clientVersion = new Version(AMQP.PROTOCOL.MAJOR, AMQP.PROTOCOL.MINOR); /** * The special channel 0 (<i>not</i> managed by the * <code><b>_channelManager</b></code>) */ private final AMQChannel _channel0 = new AMQChannel(this, 0) { @Override public boolean processAsync(Command c) throws IOException { return getConnection().processControlCommand(c); } }; protected ConsumerWorkService _workService = null; /** Frame source/sink */ private final FrameHandler _frameHandler; /** Flag controlling the main driver loop's termination */ private volatile boolean _running = false; /** Handler for (uncaught) exceptions that crop up in the {@link MainLoop}. */ private final ExceptionHandler _exceptionHandler; /** * Object used for blocking main application thread when doing all the * necessary connection shutdown operations */ private final BlockingCell<Object> _appContinuation = new BlockingCell<Object>(); /** * Flag indicating whether the client received Connection.Close message from * the broker */ private volatile boolean _brokerInitiatedShutdown; /** Flag indicating we are still negotiating the connection in start */ private volatile boolean _inConnectionNegotiation; /** Manages heart-beat sending for this connection */ private HeartbeatSender _heartbeatSender; private final String _virtualHost; private final Map<String, Object> _clientProperties; private final SaslConfig saslConfig; private final int requestedHeartbeat; private final int requestedChannelMax; private final int requestedFrameMax; private final int shutdownTimeout; private final String username; private final String password; private final Collection<BlockedListener> blockedListeners = new CopyOnWriteArrayList<BlockedListener>(); /* State modified after start - all volatile */ /** Maximum frame length, or zero if no limit is set */ private volatile int _frameMax = 0; /** Count of socket-timeouts that have happened without any incoming frames */ private volatile int _missedHeartbeats = 0; /** Currently-configured heart-beat interval, in seconds. 0 meaning none. */ private volatile int _heartbeat = 0; /** Object that manages a set of channels */ private volatile ChannelManager _channelManager; /** Saved server properties field from connection.start */ private volatile Map<String, Object> _serverProperties; /** * Protected API - respond, in the driver thread, to a ShutdownSignal. * * @param channel * the channel to disconnect */ public final void disconnectChannel(ChannelN channel) { ChannelManager cm = _channelManager; if (cm != null) cm.releaseChannelNumber(channel); } private final void ensureIsOpen() throws AlreadyClosedException { if (!isOpen()) { throw new AlreadyClosedException(getCloseReason()); } } /** {@inheritDoc} */ public InetAddress getAddress() { return _frameHandler.getAddress(); } public InetAddress getLocalAddress() { return _frameHandler.getLocalAddress(); } /** {@inheritDoc} */ public int getPort() { return _frameHandler.getPort(); } public int getLocalPort() { return _frameHandler.getLocalPort(); } public FrameHandler getFrameHandler() { return _frameHandler; } /** {@inheritDoc} */ public Map<String, Object> getServerProperties() { return _serverProperties; } /** * Construct a new connection * * @param params * parameters for it */ public AMQConnection(ConnectionParams params, FrameHandler frameHandler) { checkPreconditions(); this.username = params.getUsername(); this.password = params.getPassword(); this._frameHandler = frameHandler; this._virtualHost = params.getVirtualHost(); this._exceptionHandler = params.getExceptionHandler(); this._clientProperties = new HashMap<String, Object>(params.getClientProperties()); this.requestedFrameMax = params.getRequestedFrameMax(); this.requestedChannelMax = params.getRequestedChannelMax(); this.requestedHeartbeat = params.getRequestedHeartbeat(); this.shutdownTimeout = params.getShutdownTimeout(); this.saslConfig = params.getSaslConfig(); this.executor = params.getExecutor(); this.threadFactory = params.getThreadFactory(); this._channelManager = null; this._brokerInitiatedShutdown = false; this._inConnectionNegotiation = true; // we start out waiting for the // first protocol response } private void initializeConsumerWorkService() { this._workService = new ConsumerWorkService(executor, threadFactory, shutdownTimeout); } private void initializeHeartbeatSender() { this._heartbeatSender = new HeartbeatSender(_frameHandler, threadFactory); } /** * Start up the connection, including the MainLoop thread. Sends the * protocol version negotiation header, and runs through * Connection.Start/.StartOk, Connection.Tune/.TuneOk, and then calls * Connection.Open and waits for the OpenOk. Sets heart-beat and frame max * values after tuning has taken place. * * @throws IOException * if an error is encountered either before, or during, protocol * negotiation; sub-classes * {@link ProtocolVersionMismatchException} and * {@link PossibleAuthenticationFailureException} will be thrown * in the corresponding circumstances. * {@link AuthenticationFailureException} will be thrown if the * broker closes the connection with ACCESS_REFUSED. If an * exception is thrown, connection resources allocated can all * be garbage collected when the connection object is no longer * referenced. */ public void start() throws IOException { initializeConsumerWorkService(); initializeHeartbeatSender(); this._running = true; // Make sure that the first thing we do is to send the header, // which should cause any socket errors to show up for us, rather // than risking them pop out in the MainLoop AMQChannel.SimpleBlockingRpcContinuation connStartBlocker = new AMQChannel.SimpleBlockingRpcContinuation(); // We enqueue an RPC continuation here without sending an RPC // request, since the protocol specifies that after sending // the version negotiation header, the client (connection // initiator) is to wait for a connection.start method to // arrive. _channel0.enqueueRpc(connStartBlocker); try { // The following two lines are akin to AMQChannel's // transmit() method for this pseudo-RPC. _frameHandler.setTimeout(HANDSHAKE_TIMEOUT); _frameHandler.sendHeader(); } catch (IOException ioe) { _frameHandler.close(); throw ioe; } /* * Custom action during handshake */ mHandshakeAction.doAction(); // start the main loop going MainLoop loop = new MainLoop(); final String name = "AMQP Connection " + getHostAddress() + ":" + getPort(); mainLoopThread = Environment.newThread(threadFactory, loop, name); mainLoopThread.start(); // after this point clear-up of MainLoop is triggered by closing the // frameHandler. AMQP.Connection.Start connStart = null; AMQP.Connection.Tune connTune = null; try { connStart = (AMQP.Connection.Start) connStartBlocker.getReply().getMethod(); /* * Custom action during handshake */ mHandshakeAction.doAction(); _serverProperties = Collections.unmodifiableMap(connStart.getServerProperties()); Version serverVersion = new Version(connStart.getVersionMajor(), connStart.getVersionMinor()); if (!Version.checkVersion(clientVersion, serverVersion)) { throw new ProtocolVersionMismatchException(clientVersion, serverVersion); } String[] mechanisms = connStart.getMechanisms().toString().split(" "); SaslMechanism sm = this.saslConfig.getSaslMechanism(mechanisms); if (sm == null) { throw new IOException("No compatible authentication mechanism found - " + "server offered [" + connStart.getMechanisms() + "]"); } LongString challenge = null; LongString response = sm.handleChallenge(null, this.username, this.password); /* * Custom action during handshake */ mHandshakeAction.doAction(); do { Method method = (challenge == null) ? new AMQP.Connection.StartOk.Builder().clientProperties(_clientProperties) .mechanism(sm.getName()).response(response).build() : new AMQP.Connection.SecureOk.Builder().response(response).build(); /* * Custom action during handshake */ mHandshakeAction.doAction(); try { Method serverResponse = _channel0.rpc(method).getMethod(); /* * Custom action during handshake */ mHandshakeAction.doAction(); if (serverResponse instanceof AMQP.Connection.Tune) { connTune = (AMQP.Connection.Tune) serverResponse; } else { challenge = ((AMQP.Connection.Secure) serverResponse).getChallenge(); response = sm.handleChallenge(challenge, this.username, this.password); } } catch (ShutdownSignalException e) { Method shutdownMethod = e.getReason(); if (shutdownMethod instanceof AMQP.Connection.Close) { AMQP.Connection.Close shutdownClose = (AMQP.Connection.Close) shutdownMethod; if (shutdownClose.getReplyCode() == AMQP.ACCESS_REFUSED) { throw new AuthenticationFailureException(shutdownClose.getReplyText()); } } throw new PossibleAuthenticationFailureException(e); } } while (connTune == null); } catch (ShutdownSignalException sse) { _frameHandler.close(); throw AMQChannel.wrap(sse); } catch (IOException ioe) { _frameHandler.close(); throw ioe; } try { int channelMax = negotiateChannelMax(this.requestedChannelMax, connTune.getChannelMax()); _channelManager = instantiateChannelManager(channelMax, threadFactory); int frameMax = negotiatedMaxValue(this.requestedFrameMax, connTune.getFrameMax()); this._frameMax = frameMax; int heartbeat = negotiatedMaxValue(this.requestedHeartbeat, connTune.getHeartbeat()); setHeartbeat(heartbeat); /* * Custom action during handshake */ mHandshakeAction.doAction(); _channel0.transmit(new AMQP.Connection.TuneOk.Builder().channelMax(channelMax).frameMax(frameMax) .heartbeat(heartbeat).build()); /* * Custom action during handshake */ mHandshakeAction.doAction(); _channel0.exnWrappingRpc(new AMQP.Connection.Open.Builder().virtualHost(_virtualHost).build()); } catch (IOException ioe) { _heartbeatSender.shutdown(); _frameHandler.close(); throw ioe; } catch (ShutdownSignalException sse) { _heartbeatSender.shutdown(); _frameHandler.close(); throw AMQChannel.wrap(sse); } // We can now respond to errors having finished tailoring the connection this._inConnectionNegotiation = false; return; } protected ChannelManager instantiateChannelManager(int channelMax, ThreadFactory threadFactory) { return new ChannelManager(this._workService, channelMax, threadFactory); } /** * Private API, allows for easier simulation of bogus clients. */ protected int negotiateChannelMax(int requestedChannelMax, int serverMax) { return negotiatedMaxValue(requestedChannelMax, serverMax); } /** * Private API - check required preconditions and protocol invariants */ private static final void checkPreconditions() { AMQCommand.checkPreconditions(); } /** {@inheritDoc} */ public int getChannelMax() { ChannelManager cm = _channelManager; if (cm == null) return 0; return cm.getChannelMax(); } /** {@inheritDoc} */ public int getFrameMax() { return _frameMax; } /** {@inheritDoc} */ public int getHeartbeat() { return _heartbeat; } /** * Protected API - set the heartbeat timeout. Should only be called during * tuning. */ public void setHeartbeat(int heartbeat) { try { _heartbeatSender.setHeartbeat(heartbeat); _heartbeat = heartbeat; // Divide by four to make the maximum unwanted delay in // sending a timeout be less than a quarter of the // timeout setting. _frameHandler.setTimeout(heartbeat * 1000 / 4); } catch (SocketException se) { // should do more here? } } /** * Makes it possible to override thread factory that is used to instantiate * connection network I/O loop. Only necessary in the environments with * restricted * * @param threadFactory */ public void setThreadFactory(ThreadFactory threadFactory) { this.threadFactory = threadFactory; } /** * @return Thread factory used by this connection. */ public ThreadFactory getThreadFactory() { return threadFactory; } public Map<String, Object> getClientProperties() { return new HashMap<String, Object>(_clientProperties); } /** * Protected API - retrieve the current ExceptionHandler */ public ExceptionHandler getExceptionHandler() { return _exceptionHandler; } /** * Public API * * @return true if this work service instance uses its own executor (as * opposed to a shared one) */ public boolean willShutDownConsumerExecutor() { return this._workService.usesPrivateExecutor(); } /** Public API - {@inheritDoc} */ public Channel createChannel(int channelNumber) throws IOException { ensureIsOpen(); ChannelManager cm = _channelManager; if (cm == null) return null; return null; // return cm.createChannel(this, channelNumber); } /** Public API - {@inheritDoc} */ public Channel createChannel() throws IOException { ensureIsOpen(); ChannelManager cm = _channelManager; if (cm == null) return null; return null; // return cm.createChannel(this); } /** * Public API - sends a frame directly to the broker. */ public void writeFrame(Frame f) throws IOException { _frameHandler.writeFrame(f); _heartbeatSender.signalActivity(); } /** * Public API - flush the output buffers */ public void flush() throws IOException { _frameHandler.flush(); } private static final int negotiatedMaxValue(int clientValue, int serverValue) { return (clientValue == 0 || serverValue == 0) ? Math.max(clientValue, serverValue) : Math.min(clientValue, serverValue); } private class MainLoop implements Runnable { /** * Channel reader thread main loop. Reads a frame, and if it is not a * heartbeat frame, dispatches it to the channel it refers to. Continues * running until the "running" flag is set false by shutdown(). */ public void run() { try { while (_running) { Frame frame = _frameHandler.readFrame(); if (frame != null) { _missedHeartbeats = 0; if (frame.type == AMQP.FRAME_HEARTBEAT) { // Ignore it: we've already just reset the heartbeat // counter. } else { if (frame.channel == 0) { // the special channel _channel0.handleFrame(frame); } else { if (isOpen()) { // If we're still _running, but not // isOpen(), then we // must be quiescing, which means any // inbound frames // for non-zero channels (and any inbound // commands on // channel zero that aren't // Connection.CloseOk) must // be discarded. ChannelManager cm = _channelManager; if (cm != null) { cm.getChannel(frame.channel).handleFrame(frame); } } } } } else { // Socket timeout waiting for a frame. // Maybe missed heartbeat. handleSocketTimeout(); } } } catch (EOFException ex) { if (!_brokerInitiatedShutdown) shutdown(null, false, ex, true); } catch (Throwable ex) { _exceptionHandler.handleUnexpectedConnectionDriverException(AMQConnection.this, ex); shutdown(null, false, ex, true); } finally { // Finally, shut down our underlying data connection. _frameHandler.close(); _appContinuation.set(null); notifyListeners(); } } } /** * Called when a frame-read operation times out * * @throws MissedHeartbeatException * if heart-beats have been missed */ private void handleSocketTimeout() throws SocketTimeoutException { if (_inConnectionNegotiation) { throw new SocketTimeoutException("Timeout during Connection negotiation"); } if (_heartbeat == 0) { // No heart-beating return; } // We check against 8 = 2 * 4 because we need to wait for at // least two complete heartbeat setting intervals before // complaining, and we've set the socket timeout to a quarter // of the heartbeat setting in setHeartbeat above. if (++_missedHeartbeats > (2 * 4)) { throw new MissedHeartbeatException("Heartbeat missing with heartbeat = " + _heartbeat + " seconds"); } } /** * Handles incoming control commands on channel zero. * * @see ChannelN#processAsync */ @SuppressWarnings("unused") public boolean processControlCommand(Command c) throws IOException { // Similar trick to ChannelN.processAsync used here, except // we're interested in whole-connection quiescing. // See the detailed comments in ChannelN.processAsync. Method method = c.getMethod(); if (isOpen()) { if (method instanceof AMQP.Connection.Close) { handleConnectionClose(c); return true; } else if (method instanceof AMQP.Connection.Blocked) { AMQP.Connection.Blocked blocked = (AMQP.Connection.Blocked) method; try { for (BlockedListener l : this.blockedListeners) { l.handleBlocked(blocked.getReason()); } } catch (Throwable ex) { getExceptionHandler().handleBlockedListenerException(this, ex); } return true; } else if (method instanceof AMQP.Connection.Unblocked) { try { for (BlockedListener l : this.blockedListeners) { l.handleUnblocked(); } } catch (Throwable ex) { getExceptionHandler().handleBlockedListenerException(this, ex); } return true; } else { return false; } } else { if (method instanceof AMQP.Connection.Close) { // Already shutting down, so just send back a CloseOk. try { _channel0.quiescingTransmit(new AMQP.Connection.CloseOk.Builder().build()); } catch (IOException _e) { } // ignore return true; } else if (method instanceof AMQP.Connection.CloseOk) { // It's our final "RPC". Time to shut down. _running = false; // If Close was sent from within the MainLoop we // will not have a continuation to return to, so // we treat this as processed in that case. return !_channel0.isOutstandingRpc(); } else { // Ignore all others. return true; } } } @SuppressWarnings("unused") public void handleConnectionClose(Command closeCommand) { ShutdownSignalException sse = shutdown(closeCommand.getMethod(), false, null, _inConnectionNegotiation); try { _channel0.quiescingTransmit(new AMQP.Connection.CloseOk.Builder().build()); } catch (IOException _e) { } // ignore _brokerInitiatedShutdown = true; SocketCloseWait scw = new SocketCloseWait(sse); final String name = "AMQP Connection Closing Monitor " + getHostAddress() + ":" + getPort(); Thread waiter = Environment.newThread(threadFactory, scw, name); waiter.start(); } private class SocketCloseWait implements Runnable { private final ShutdownSignalException cause; public SocketCloseWait(ShutdownSignalException sse) { cause = sse; } public void run() { try { _appContinuation.uninterruptibleGet(); } finally { _running = false; _channel0.notifyOutstandingRpc(cause); } } } /** * Protected API - causes all attached channels to terminate (shutdown) with * a ShutdownSignal built from the argument, and stops this connection from * accepting further work from the application. * {@link com.rabbitmq.client.ShutdownListener ShutdownListener}s for the * connection are notified when the main loop terminates. * * @param reason * description of reason for the exception * @param initiatedByApplication * true if caused by a client command * @param cause * trigger exception which caused shutdown * @param notifyRpc * true if outstanding rpc should be informed of shutdown * @return a shutdown signal built using the given arguments */ public ShutdownSignalException shutdown(Method reason, boolean initiatedByApplication, Throwable cause, boolean notifyRpc) { ShutdownSignalException sse = startShutdown(reason, initiatedByApplication, cause, notifyRpc); finishShutdown(sse); return sse; } private ShutdownSignalException startShutdown(Method reason, boolean initiatedByApplication, Throwable cause, boolean notifyRpc) { ShutdownSignalException sse = new ShutdownSignalException(true, initiatedByApplication, reason, this); sse.initCause(cause); if (!setShutdownCauseIfOpen(sse)) { if (initiatedByApplication) throw new AlreadyClosedException(getCloseReason(), cause); } // stop any heartbeating _heartbeatSender.shutdown(); _channel0.processShutdownSignal(sse, !initiatedByApplication, notifyRpc); return sse; } private void finishShutdown(ShutdownSignalException sse) { ChannelManager cm = _channelManager; if (cm != null) cm.handleSignal(sse); } /** Public API - {@inheritDoc} */ public void close() throws IOException { close(-1); } /** Public API - {@inheritDoc} */ public void close(int timeout) throws IOException { close(AMQP.REPLY_SUCCESS, "OK", timeout); } /** Public API - {@inheritDoc} */ public void close(int closeCode, String closeMessage) throws IOException { close(closeCode, closeMessage, -1); } /** Public API - {@inheritDoc} */ public void close(int closeCode, String closeMessage, int timeout) throws IOException { close(closeCode, closeMessage, true, null, timeout, false); } /** Public API - {@inheritDoc} */ public void abort() { abort(-1); } /** Public API - {@inheritDoc} */ public void abort(int closeCode, String closeMessage) { abort(closeCode, closeMessage, -1); } /** Public API - {@inheritDoc} */ public void abort(int timeout) { abort(AMQP.REPLY_SUCCESS, "OK", timeout); } /** Public API - {@inheritDoc} */ @SuppressWarnings("unused") public void abort(int closeCode, String closeMessage, int timeout) { try { close(closeCode, closeMessage, true, null, timeout, true); } catch (IOException _e) { } // ignore } /** * Protected API - Delegates to * {@link #close(int,String,boolean,Throwable,int,boolean) the six-argument * close method}, passing -1 for the timeout, and false for the abort flag. */ public void close(int closeCode, String closeMessage, boolean initiatedByApplication, Throwable cause) throws IOException { close(closeCode, closeMessage, initiatedByApplication, cause, -1, false); } // TODO: Make this private /** * Protected API - Close this connection with the given code, message, * source and timeout value for all the close operations to complete. * Specifies if any encountered exceptions should be ignored. */ public void close(int closeCode, String closeMessage, boolean initiatedByApplication, Throwable cause, int timeout, boolean abort) throws IOException { boolean sync = !(Thread.currentThread() == mainLoopThread); try { AMQP.Connection.Close reason = new AMQP.Connection.Close.Builder().replyCode(closeCode) .replyText(closeMessage).build(); final ShutdownSignalException sse = startShutdown(reason, initiatedByApplication, cause, true); if (sync) { /* * Do custom action */ mHandshakeAction.doAction(); BlockingRpcContinuation<AMQCommand> k = new BlockingRpcContinuation<AMQCommand>() { @Override public AMQCommand transformReply(AMQCommand command) { AMQConnection.this.finishShutdown(sse); return command; } }; _channel0.quiescingRpc(reason, k); /* * Do custom action */ mHandshakeAction.doAction(); k.getReply(timeout); } else { _channel0.quiescingTransmit(reason); } } catch (TimeoutException tte) { if (!abort) { ShutdownSignalException sse = new ShutdownSignalException(true, true, null, this); sse.initCause(cause); throw sse; } } catch (ShutdownSignalException sse) { if (!abort) throw sse; } catch (IOException ioe) { if (!abort) throw ioe; } finally { if (sync) _frameHandler.close(); } } @Override public String toString() { return "amqp://" + this.username + "@" + getHostAddress() + ":" + getPort() + _virtualHost; } private String getHostAddress() { return getAddress() == null ? null : getAddress().getHostAddress(); } public void addBlockedListener(BlockedListener listener) { blockedListeners.add(listener); } public boolean removeBlockedListener(BlockedListener listener) { return blockedListeners.remove(listener); } public void clearBlockedListeners() { blockedListeners.clear(); } }