23
23
import java .lang .reflect .Proxy ;
24
24
import java .net .URI ;
25
25
import java .util .Arrays ;
26
+ import java .util .Collection ;
26
27
import java .util .HashMap ;
27
28
import java .util .HashSet ;
28
29
import java .util .LinkedList ;
32
33
import java .util .Properties ;
33
34
import java .util .Set ;
34
35
import java .util .concurrent .BlockingDeque ;
36
+ import java .util .concurrent .ConcurrentHashMap ;
37
+ import java .util .concurrent .ConcurrentMap ;
35
38
import java .util .concurrent .ExecutorService ;
36
39
import java .util .concurrent .Executors ;
37
40
import java .util .concurrent .LinkedBlockingDeque ;
92
95
*/
93
96
@ ManagedResource
94
97
public class CachingConnectionFactory extends AbstractConnectionFactory
95
- implements InitializingBean , ShutdownListener , PublisherCallbackChannelConnectionFactory {
98
+ implements InitializingBean , ShutdownListener {
96
99
97
100
private static final int DEFAULT_CHANNEL_CACHE_SIZE = 25 ;
98
101
@@ -123,10 +126,10 @@ public enum CacheMode {
123
126
private final Set <ChannelCachingConnectionProxy > allocatedConnections = new HashSet <>();
124
127
125
128
private final Map <ChannelCachingConnectionProxy , LinkedList <ChannelProxy >>
126
- allocatedConnectionNonTransactionalChannels = new HashMap <>();
129
+ allocatedConnectionNonTransactionalChannels = new HashMap <>();
127
130
128
131
private final Map <ChannelCachingConnectionProxy , LinkedList <ChannelProxy >>
129
- allocatedConnectionTransactionalChannels = new HashMap <>();
132
+ allocatedConnectionTransactionalChannels = new HashMap <>();
130
133
131
134
private final BlockingDeque <ChannelCachingConnectionProxy > idleConnections = new LinkedBlockingDeque <>();
132
135
@@ -245,9 +248,9 @@ private CachingConnectionFactory(com.rabbitmq.client.ConnectionFactory rabbitCon
245
248
if (!isPublisherFactory ) {
246
249
if (rabbitConnectionFactory .isAutomaticRecoveryEnabled ()) {
247
250
logger .warn ("***\n Automatic Recovery is Enabled in the provided connection factory;\n "
248
- + "while Spring AMQP is compatible with this feature, it\n "
249
- + "prefers to use its own recovery mechanisms; when this option is true, you may receive\n "
250
- + "'AutoRecoverConnectionNotCurrentlyOpenException's until the connection is recovered." );
251
+ + "while Spring AMQP is compatible with this feature, it\n "
252
+ + "prefers to use its own recovery mechanisms; when this option is true, you may receive\n "
253
+ + "'AutoRecoverConnectionNotCurrentlyOpenException's until the connection is recovered." );
251
254
}
252
255
this .publisherConnectionFactory = new CachingConnectionFactory (getRabbitConnectionFactory (),
253
256
true );
@@ -471,7 +474,7 @@ private Channel getChannel(ChannelCachingConnectionProxy connection, boolean tra
471
474
}
472
475
if (logger .isDebugEnabled ()) {
473
476
logger .debug (
474
- "Acquired permit for " + connection + ", remaining:" + checkoutPermits .availablePermits ());
477
+ "Acquired permit for " + connection + ", remaining:" + checkoutPermits .availablePermits ());
475
478
}
476
479
}
477
480
catch (InterruptedException e ) {
@@ -551,7 +554,7 @@ private Channel getChannel(ChannelCachingConnectionProxy connection, boolean tra
551
554
checkoutPermits .release ();
552
555
if (logger .isDebugEnabled ()) {
553
556
logger .debug ("Could not get channel; released permit for " + connection + ", remaining:"
554
- + checkoutPermits .availablePermits ());
557
+ + checkoutPermits .availablePermits ());
555
558
}
556
559
}
557
560
throw e ;
@@ -792,31 +795,33 @@ public void resetConnection() {
792
795
/*
793
796
* Reset the Channel cache and underlying shared Connection, to be reinitialized on next access.
794
797
*/
795
- protected void reset (List <ChannelProxy > channels , List <ChannelProxy > txChannels ) {
798
+ protected void reset (List <ChannelProxy > channels , List <ChannelProxy > txChannels ,
799
+ Map <Channel , ChannelProxy > channelsAwaitingAcks ) {
800
+
796
801
this .active = false ;
797
- synchronized (channels ) {
798
- for (ChannelProxy channel : channels ) {
799
- try {
800
- channel .close ();
801
- }
802
- catch (Exception ex ) {
803
- logger .trace ("Could not close cached Rabbit Channel" , ex );
804
- }
805
- }
806
- channels .clear ();
802
+ closeAndClear (channels );
803
+ closeAndClear (txChannels );
804
+ closeChannels (channelsAwaitingAcks .values ());
805
+ channelsAwaitingAcks .clear ();
806
+ this .active = true ;
807
+ }
808
+
809
+ protected void closeAndClear (Collection <ChannelProxy > theChannels ) {
810
+ synchronized (theChannels ) {
811
+ closeChannels (theChannels );
812
+ theChannels .clear ();
807
813
}
808
- synchronized (txChannels ) {
809
- for (ChannelProxy channel : txChannels ) {
810
- try {
811
- channel .close ();
812
- }
813
- catch (Exception ex ) {
814
- logger .trace ("Could not close cached Rabbit Channel" , ex );
815
- }
814
+ }
815
+
816
+ protected void closeChannels (Collection <ChannelProxy > theChannels ) {
817
+ for (ChannelProxy channel : theChannels ) {
818
+ try {
819
+ channel .close ();
820
+ }
821
+ catch (Exception ex ) {
822
+ logger .trace ("Could not close cached Rabbit Channel" , ex );
816
823
}
817
- txChannels .clear ();
818
824
}
819
- this .active = true ;
820
825
}
821
826
822
827
@ ManagedAttribute
@@ -829,12 +834,12 @@ public Properties getCacheProperties() {
829
834
props .setProperty ("connectionCacheSize" , Integer .toString (this .connectionCacheSize ));
830
835
props .setProperty ("openConnections" , Integer .toString (countOpenConnections ()));
831
836
props .setProperty ("idleConnections" , Integer .toString (this .idleConnections .size ()));
832
- props .setProperty ("idleConnectionsHighWater" , Integer .toString (this .connectionHighWaterMark .get ()));
837
+ props .setProperty ("idleConnectionsHighWater" , Integer .toString (this .connectionHighWaterMark .get ()));
833
838
for (ChannelCachingConnectionProxy proxy : this .allocatedConnections ) {
834
839
putConnectionName (props , proxy , ":" + proxy .getLocalPort ());
835
840
}
836
841
for (Entry <ChannelCachingConnectionProxy , LinkedList <ChannelProxy >> entry :
837
- this .allocatedConnectionTransactionalChannels .entrySet ()) {
842
+ this .allocatedConnectionTransactionalChannels .entrySet ()) {
838
843
int port = entry .getKey ().getLocalPort ();
839
844
if (port > 0 && entry .getKey ().isOpen ()) {
840
845
LinkedList <ChannelProxy > channelList = entry .getValue ();
@@ -844,7 +849,7 @@ public Properties getCacheProperties() {
844
849
}
845
850
}
846
851
for (Entry <ChannelCachingConnectionProxy , LinkedList <ChannelProxy >> entry :
847
- this .allocatedConnectionNonTransactionalChannels .entrySet ()) {
852
+ this .allocatedConnectionNonTransactionalChannels .entrySet ()) {
848
853
int port = entry .getKey ().getLocalPort ();
849
854
if (port > 0 && entry .getKey ().isOpen ()) {
850
855
LinkedList <ChannelProxy > channelList = entry .getValue ();
@@ -921,7 +926,9 @@ private final class CachedChannelInvocationHandler implements InvocationHandler
921
926
922
927
private final boolean transactional ;
923
928
924
- private volatile boolean confirmSelected = CachingConnectionFactory .this .simplePublisherConfirms ;
929
+ private final boolean confirmSelected = CachingConnectionFactory .this .simplePublisherConfirms ;
930
+
931
+ private final boolean publisherConfirms = CachingConnectionFactory .this .publisherConfirms ;
925
932
926
933
private volatile Channel target ;
927
934
@@ -959,7 +966,7 @@ else if (methodName.equals("close")) {
959
966
// Handle close method: don't pass the call on.
960
967
if (CachingConnectionFactory .this .active ) {
961
968
synchronized (this .channelList ) {
962
- if (!RabbitUtils .isPhysicalCloseRequired () &&
969
+ if (CachingConnectionFactory . this . active && !RabbitUtils .isPhysicalCloseRequired () &&
963
970
(this .channelList .size () < getChannelCacheSize ()
964
971
|| this .channelList .contains (proxy ))) {
965
972
releasePermitIfNecessary (proxy );
@@ -1056,7 +1063,7 @@ private void releasePermitIfNecessary(Object proxy) {
1056
1063
checkoutPermits .release ();
1057
1064
if (logger .isDebugEnabled ()) {
1058
1065
logger .debug ("Released permit for '" + this .theConnection + "', remaining: "
1059
- + checkoutPermits .availablePermits ());
1066
+ + checkoutPermits .availablePermits ());
1060
1067
}
1061
1068
}
1062
1069
else {
@@ -1088,13 +1095,42 @@ private void logicalClose(ChannelProxy proxy) throws Exception {
1088
1095
}
1089
1096
}
1090
1097
}
1091
- // Allow for multiple close calls...
1092
- if (!this .channelList .contains (proxy )) {
1093
- if (logger .isTraceEnabled ()) {
1094
- logger .trace ("Returning cached Channel: " + this .target );
1098
+ if (CachingConnectionFactory .this .active && this .publisherConfirms
1099
+ && proxy instanceof PublisherCallbackChannel ) {
1100
+
1101
+ this .theConnection .channelsAwaitingAcks .put (this .target , proxy );
1102
+ ((PublisherCallbackChannel ) proxy )
1103
+ .setAfterAckCallback (c ->
1104
+ returnToCache (this .theConnection .channelsAwaitingAcks .remove (c )));
1105
+ }
1106
+ else {
1107
+ returnToCache (proxy );
1108
+ }
1109
+ }
1110
+
1111
+ private void returnToCache (Channel proxy ) {
1112
+ if (proxy != null ) {
1113
+ synchronized (this .channelList ) {
1114
+ // Allow for multiple close calls...
1115
+ if (CachingConnectionFactory .this .active ) {
1116
+ if (!this .channelList .contains (proxy )) {
1117
+ if (logger .isTraceEnabled ()) {
1118
+ logger .trace ("Returning cached Channel: " + this .target );
1119
+ }
1120
+ this .channelList .addLast ((ChannelProxy ) proxy );
1121
+ setHighWaterMark ();
1122
+ }
1123
+ }
1124
+ else {
1125
+ if (proxy .isOpen ()) {
1126
+ try {
1127
+ physicalClose ();
1128
+ }
1129
+ catch (Exception e ) {
1130
+ }
1131
+ }
1132
+ }
1095
1133
}
1096
- this .channelList .addLast (proxy );
1097
- setHighWaterMark ();
1098
1134
}
1099
1135
}
1100
1136
@@ -1154,14 +1190,18 @@ private void asyncClose() {
1154
1190
catch (InterruptedException e1 ) {
1155
1191
Thread .currentThread ().interrupt ();
1156
1192
}
1157
- catch (Exception e2 ) { }
1193
+ catch (Exception e2 ) {
1194
+ }
1158
1195
finally {
1159
1196
try {
1160
1197
channel .close ();
1161
1198
}
1162
- catch (IOException e3 ) { }
1163
- catch (AlreadyClosedException e4 ) { }
1164
- catch (TimeoutException e5 ) { }
1199
+ catch (IOException e3 ) {
1200
+ }
1201
+ catch (AlreadyClosedException e4 ) {
1202
+ }
1203
+ catch (TimeoutException e5 ) {
1204
+ }
1165
1205
catch (ShutdownSignalException e6 ) {
1166
1206
if (!RabbitUtils .isNormalShutdown (e6 )) {
1167
1207
logger .debug ("Unexpected exception on deferred close" , e6 );
@@ -1177,6 +1217,8 @@ private class ChannelCachingConnectionProxy implements ConnectionProxy { // NOSO
1177
1217
1178
1218
private final AtomicBoolean closeNotified = new AtomicBoolean (false );
1179
1219
1220
+ private final ConcurrentMap <Channel , ChannelProxy > channelsAwaitingAcks = new ConcurrentHashMap <>();
1221
+
1180
1222
private volatile Connection target ;
1181
1223
1182
1224
ChannelCachingConnectionProxy (Connection target ) {
@@ -1248,11 +1290,12 @@ private int countOpenIdleConnections() {
1248
1290
public void destroy () {
1249
1291
if (CachingConnectionFactory .this .cacheMode == CacheMode .CHANNEL ) {
1250
1292
reset (CachingConnectionFactory .this .cachedChannelsNonTransactional ,
1251
- CachingConnectionFactory .this .cachedChannelsTransactional );
1293
+ CachingConnectionFactory .this .cachedChannelsTransactional , this . channelsAwaitingAcks );
1252
1294
}
1253
1295
else {
1254
1296
reset (CachingConnectionFactory .this .allocatedConnectionNonTransactionalChannels .get (this ),
1255
- CachingConnectionFactory .this .allocatedConnectionTransactionalChannels .get (this ));
1297
+ CachingConnectionFactory .this .allocatedConnectionTransactionalChannels .get (this ),
1298
+ this .channelsAwaitingAcks );
1256
1299
}
1257
1300
if (this .target != null ) {
1258
1301
RabbitUtils .closeConnection (this .target );
@@ -1289,8 +1332,8 @@ public int getLocalPort() {
1289
1332
@ Override
1290
1333
public String toString () {
1291
1334
return "Proxy@" + ObjectUtils .getIdentityHexString (this ) + " "
1292
- + (CachingConnectionFactory .this .cacheMode == CacheMode .CHANNEL ? "Shared " : "Dedicated " )
1293
- + "Rabbit Connection: " + this .target ;
1335
+ + (CachingConnectionFactory .this .cacheMode == CacheMode .CHANNEL ? "Shared " : "Dedicated " )
1336
+ + "Rabbit Connection: " + this .target ;
1294
1337
}
1295
1338
1296
1339
}
0 commit comments