2828import java .io .EOFException ;
2929import java .io .IOException ;
3030import java .net .SocketException ;
31+ import java .util .ArrayList ;
32+ import java .util .List ;
3133import java .util .Map ;
3234
3335import com .rabbitmq .client .AMQP ;
3436import com .rabbitmq .client .Address ;
37+ import com .rabbitmq .client .AlreadyClosedException ;
3538import com .rabbitmq .client .Channel ;
3639import com .rabbitmq .client .Command ;
3740import com .rabbitmq .client .Connection ;
3841import com .rabbitmq .client .ConnectionParameters ;
3942import com .rabbitmq .client .MissedHeartbeatException ;
4043import com .rabbitmq .client .RedirectException ;
44+ import com .rabbitmq .client .ShutdownListener ;
4145import com .rabbitmq .client .ShutdownSignalException ;
4246import com .rabbitmq .utility .Utility ;
4347
@@ -100,7 +104,11 @@ public class AMQConnection implements Connection {
100104
101105 /** Handler for (otherwise-unhandled) exceptions that crop up in the mainloop. */
102106 public final ExceptionHandler _exceptionHandler ;
103-
107+
108+ /** List of all shutdown listeners associated with the connection */
109+ public List <ShutdownListener > listeners
110+ = new ArrayList <ShutdownListener >();
111+
104112 /**
105113 * Protected API - respond, in the driver thread, to a ShutdownSignal.
106114 * @param channelNumber the number of the channel to disconnect
@@ -109,15 +117,19 @@ public final void disconnectChannel(int channelNumber) {
109117 _channelManager .disconnectChannel (channelNumber );
110118 }
111119
120+ /**
121+ * Public API - Determine whether the connection is open
122+ * @return true if haven't yet received shutdown signal, false otherwise
123+ */
112124 public boolean isOpen () {
113125 return _shutdownCause == null ;
114126 }
115127
116128 public void ensureIsOpen ()
117- throws IllegalStateException
129+ throws AlreadyClosedException
118130 {
119131 if (!isOpen ()) {
120- throw new IllegalStateException ("Attempt to use closed connection" );
132+ throw new AlreadyClosedException ("Attempt to use closed connection" );
121133 }
122134 }
123135
@@ -340,15 +352,13 @@ public Address[] open(final ConnectionParameters params, boolean insist)
340352 _frameHandler .setTimeout (HANDSHAKE_TIMEOUT );
341353 _frameHandler .sendHeader ();
342354
343- if (!isOpen ()) {
344- // See bug 17389. The MainLoop could have shut down already in
345- // which case we don't want to wait forever for a reply.
346-
347- // There is no race if the MainLoop shuts down after enqueuing
348- // the RPC because if that happens the channel will correctly
349- // pass the exception into RPC, waking it up.
350- throw _shutdownCause ;
351- }
355+ // See bug 17389. The MainLoop could have shut down already in
356+ // which case we don't want to wait forever for a reply.
357+
358+ // There is no race if the MainLoop shuts down after enqueuing
359+ // the RPC because if that happens the channel will correctly
360+ // pass the exception into RPC, waking it up.
361+ ensureIsOpen ();
352362
353363 AMQP .Connection .Start connStart =
354364 (AMQP .Connection .Start ) connStartBlocker .getReply ().getMethod ();
@@ -561,6 +571,7 @@ public void handleConnectionClose(Command closeCommand) {
561571 Utility .emptyStatement ();
562572 }
563573 shutdown (closeCommand , false , null );
574+ notifyListeners ();
564575 }
565576
566577 /**
@@ -616,6 +627,56 @@ public void close(int closeCode,
616627 } finally {
617628 _running = false ;
618629 }
630+ notifyListeners ();
631+ }
632+
633+ /**
634+ * Private API - notify the listeners attached to this connection
635+ * @see com.rabbitmq.client.ShutdownListener
636+ */
637+ public void notifyListeners ()
638+ {
639+ synchronized (listeners ) {
640+ for (ShutdownListener l : listeners )
641+ l .service (getCloseReason ());
642+ }
643+ }
644+
645+ /**
646+ * Public API - Add shutdown listener fired when closing the connection
647+ * @see com.rabbitmq.client.Connection#addShutdownListener()
648+ */
649+ public void addShutdownListener (ShutdownListener listener )
650+ {
651+
652+ boolean closed = false ;
653+ synchronized (listeners ) {
654+ closed = !isOpen ();
655+ listeners .add (listener );
656+ }
657+ if (closed )
658+ listener .service (_shutdownCause );
659+ }
660+
661+ /**
662+ * Public API - Remove shutdown listener for this connection
663+ * Removing only the first found object
664+ * @see com.rabbitmq.client.Connection#removeShutdownListener()
665+ */
666+ public void removeShutdownListener (ShutdownListener listener )
667+ {
668+ synchronized (listeners ) {
669+ listeners .remove (listener );
670+ }
671+ }
672+
673+ /**
674+ * Public API - Get reason for shutdown, or null if open
675+ * @see com.rabbitmq.client.Connection#getShutdownReason()
676+ */
677+ public ShutdownSignalException getCloseReason ()
678+ {
679+ return _shutdownCause ;
619680 }
620681
621682 @ Override public String toString () {
0 commit comments