/** * 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 ignored) { } // 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; } } }
/** * 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) { BlockingRpcContinuation<AMQCommand> k = new BlockingRpcContinuation<AMQCommand>() { @Override public AMQCommand transformReply(AMQCommand command) { AMQConnection.this.finishShutdown(sse); return command; } }; _channel0.quiescingRpc(reason, k); 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(); } }
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; }
@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 ignored) { } // ignore _brokerInitiatedShutdown = true; SocketCloseWait scw = new SocketCloseWait(sse); // if shutdown executor is configured, use it. Otherwise // execut socket close monitor the old fashioned way. // see rabbitmq/rabbitmq-java-client#91 if (shutdownExecutor != null) { shutdownExecutor.execute(scw); } else { final String name = "RabbitMQ connection shutdown monitor " + getHostAddress() + ":" + getPort(); Thread waiter = Environment.newThread(threadFactory, scw, name); waiter.start(); } }
/** * 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, TimeoutException { 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(handshakeTimeout); _frameHandler.sendHeader(); } catch (IOException ioe) { _frameHandler.close(); throw ioe; } // 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; AMQP.Connection.Tune connTune = null; try { connStart = (AMQP.Connection.Start) connStartBlocker.getReply(handshakeTimeout / 2).getMethod(); _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); 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(); try { Method serverResponse = _channel0.rpc(method, handshakeTimeout / 2).getMethod(); 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 (TimeoutException te) { _frameHandler.close(); throw te; } 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); _channel0.transmit( new AMQP.Connection.TuneOk.Builder() .channelMax(channelMax) .frameMax(frameMax) .heartbeat(heartbeat) .build()); _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; }