Skip to content

Commit 98cb7a1

Browse files
garyrussellartembilan
authored andcommitted
GH-1049: Support SaslConfig in logging appenders
Resolves #1049 **cherry-pick to 2.1.x** # Conflicts: # spring-rabbit/src/test/java/org/springframework/amqp/rabbit/log4j2/AmqpAppenderTests.java # spring-rabbit/src/test/java/org/springframework/amqp/rabbit/logback/AmqpAppenderTests.java
1 parent 9c303d2 commit 98cb7a1

File tree

10 files changed

+309
-41
lines changed

10 files changed

+309
-41
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitConnectionFactoryBean.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,16 @@ public void setTrustStoreAlgorithm(String trustStoreAlgorithm) {
658658
this.trustStoreAlgorithm = trustStoreAlgorithm;
659659
}
660660

661+
/**
662+
* Access the connection factory to set any other properties not supported by
663+
* this factory bean.
664+
* @return the connection factory.
665+
* @since 1.7.14
666+
*/
667+
public ConnectionFactory getRabbitConnectionFactory() {
668+
return this.connectionFactory;
669+
}
670+
661671
@Override
662672
public void afterPropertiesSet() {
663673
try {

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitUtils.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@
3030
import com.rabbitmq.client.AMQP;
3131
import com.rabbitmq.client.AlreadyClosedException;
3232
import com.rabbitmq.client.Channel;
33+
import com.rabbitmq.client.DefaultSaslConfig;
34+
import com.rabbitmq.client.JDKSaslConfig;
3335
import com.rabbitmq.client.Method;
36+
import com.rabbitmq.client.SaslConfig;
3437
import com.rabbitmq.client.ShutdownSignalException;
38+
import com.rabbitmq.client.impl.CRDemoMechanism;
3539
import com.rabbitmq.client.impl.recovery.AutorecoveringChannel;
3640

3741
/**
@@ -369,4 +373,34 @@ public static int getMaxFrame(ConnectionFactory connectionFactory) {
369373
return -1;
370374
}
371375

376+
/**
377+
* Convert a String value to a {@link SaslConfig}.
378+
* Valid string values:
379+
* <ul>
380+
* <li>{@code DefaultSaslConfig.PLAIN}</li>
381+
* <li>{@code DefaultSaslConfig.EXTERNAL}</li>
382+
* <li>{@code JDKSaslConfig}</li>
383+
* <li>{@code CRDemoSaslConfig}</li>
384+
* </ul>
385+
* @param saslConfig the string value.
386+
* @param connectionFactory the connection factory to get the name, pw, host.
387+
* @return the saslConfig.
388+
*/
389+
public static SaslConfig stringToSaslConfig(String saslConfig,
390+
com.rabbitmq.client.ConnectionFactory connectionFactory) {
391+
392+
switch (saslConfig) {
393+
case "DefaultSaslConfig.PLAIN":
394+
return DefaultSaslConfig.PLAIN;
395+
case "DefaultSaslConfig.EXTERNAL":
396+
return DefaultSaslConfig.EXTERNAL;
397+
case "JDKSaslConfig":
398+
return new JDKSaslConfig(connectionFactory);
399+
case "CRDemoSaslConfig":
400+
return new CRDemoMechanism.CRDemoSaslConfig();
401+
default:
402+
throw new IllegalStateException("Unrecognized SaslConfig: " + saslConfig);
403+
}
404+
}
405+
372406
}

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/log4j2/AmqpAppender.java

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.util.Calendar;
2525
import java.util.Map;
2626
import java.util.Map.Entry;
27-
import java.util.Optional;
2827
import java.util.Timer;
2928
import java.util.TimerTask;
3029
import java.util.UUID;
@@ -65,15 +64,18 @@
6564
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
6665
import org.springframework.amqp.rabbit.connection.ConnectionFactoryConfigurationUtils;
6766
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
67+
import org.springframework.amqp.rabbit.connection.RabbitUtils;
6868
import org.springframework.amqp.rabbit.core.DeclareExchangeConnectionListener;
6969
import org.springframework.amqp.rabbit.core.RabbitAdmin;
7070
import org.springframework.amqp.rabbit.core.RabbitTemplate;
71+
import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator;
7172
import org.springframework.amqp.utils.JavaUtils;
7273
import org.springframework.core.io.Resource;
7374
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
7475
import org.springframework.retry.RetryPolicy;
7576
import org.springframework.retry.policy.SimpleRetryPolicy;
7677
import org.springframework.retry.support.RetryTemplate;
78+
import org.springframework.util.Assert;
7779
import org.springframework.util.StringUtils;
7880

7981
import com.rabbitmq.client.ConnectionFactory;
@@ -166,6 +168,7 @@ public static AmqpAppender createAppender(// NOSONAR NCSS line count
166168
@PluginAttribute("trustStore") String trustStore,
167169
@PluginAttribute("trustStorePassphrase") String trustStorePassphrase,
168170
@PluginAttribute("trustStoreType") String trustStoreType,
171+
@PluginAttribute("saslConfig") String saslConfig,
169172
@PluginAttribute("senderPoolSize") int senderPoolSize,
170173
@PluginAttribute("maxSenderRetries") int maxSenderRetries,
171174
@PluginAttribute("applicationId") String applicationId,
@@ -195,41 +198,43 @@ public static AmqpAppender createAppender(// NOSONAR NCSS line count
195198
theLayout = PatternLayout.createDefaultLayout();
196199
}
197200
AmqpManager manager = new AmqpManager(configuration.getLoggerContext(), name);
198-
manager.uri = uri;
199-
manager.host = host;
200-
Optional.ofNullable(port).ifPresent(v -> manager.port = Integers.parseInt(v));
201-
manager.addresses = addresses;
202-
manager.username = user;
203-
manager.password = password;
204-
manager.virtualHost = virtualHost;
205-
manager.useSsl = useSsl;
206-
manager.verifyHostname = verifyHostname;
207-
manager.sslAlgorithm = sslAlgorithm;
208-
manager.sslPropertiesLocation = sslPropertiesLocation;
209-
manager.keyStore = keyStore;
210-
manager.keyStorePassphrase = keyStorePassphrase;
211-
manager.keyStoreType = keyStoreType;
212-
manager.trustStore = trustStore;
213-
manager.trustStorePassphrase = trustStorePassphrase;
214-
manager.trustStoreType = trustStoreType;
215-
manager.senderPoolSize = senderPoolSize;
216-
manager.maxSenderRetries = maxSenderRetries;
217-
manager.applicationId = applicationId;
218-
manager.routingKeyPattern = routingKeyPattern;
219-
manager.generateId = generateId;
220-
manager.deliveryMode = MessageDeliveryMode.valueOf(deliveryMode);
221-
manager.exchangeName = exchange;
222-
manager.exchangeType = exchangeType;
223-
manager.declareExchange = declareExchange;
224-
manager.durable = durable;
225-
manager.autoDelete = autoDelete;
226-
manager.contentType = contentType;
227-
manager.contentEncoding = contentEncoding;
228-
manager.connectionName = connectionName;
229-
manager.clientConnectionProperties = clientConnectionProperties;
230-
manager.charset = charset;
231-
manager.async = async;
232-
manager.addMdcAsHeaders = addMdcAsHeaders;
201+
JavaUtils.INSTANCE
202+
.acceptIfNotNull(uri, value -> manager.uri = value)
203+
.acceptIfNotNull(host, value -> manager.host = value)
204+
.acceptIfNotNull(port, value -> manager.port = Integers.parseInt(value))
205+
.acceptIfNotNull(addresses, value -> manager.addresses = value)
206+
.acceptIfNotNull(user, value -> manager.username = value)
207+
.acceptIfNotNull(password, value -> manager.password = value)
208+
.acceptIfNotNull(virtualHost, value -> manager.virtualHost = value)
209+
.acceptIfNotNull(useSsl, value -> manager.useSsl = value)
210+
.acceptIfNotNull(verifyHostname, value -> manager.verifyHostname = value)
211+
.acceptIfNotNull(sslAlgorithm, value -> manager.sslAlgorithm = value)
212+
.acceptIfNotNull(sslPropertiesLocation, value -> manager.sslPropertiesLocation = value)
213+
.acceptIfNotNull(keyStore, value -> manager.keyStore = value)
214+
.acceptIfNotNull(keyStorePassphrase, value -> manager.keyStorePassphrase = value)
215+
.acceptIfNotNull(keyStoreType, value -> manager.keyStoreType = value)
216+
.acceptIfNotNull(trustStore, value -> manager.trustStore = value)
217+
.acceptIfNotNull(trustStorePassphrase, value -> manager.trustStorePassphrase = value)
218+
.acceptIfNotNull(trustStoreType, value -> manager.trustStoreType = value)
219+
.acceptIfNotNull(saslConfig, value -> manager.saslConfig = value)
220+
.acceptIfNotNull(senderPoolSize, value -> manager.senderPoolSize = value)
221+
.acceptIfNotNull(maxSenderRetries, value -> manager.maxSenderRetries = value)
222+
.acceptIfNotNull(applicationId, value -> manager.applicationId = value)
223+
.acceptIfNotNull(routingKeyPattern, value -> manager.routingKeyPattern = value)
224+
.acceptIfNotNull(generateId, value -> manager.generateId = value)
225+
.acceptIfNotNull(deliveryMode, value -> manager.deliveryMode = MessageDeliveryMode.valueOf(deliveryMode))
226+
.acceptIfNotNull(exchange, value -> manager.exchangeName = value)
227+
.acceptIfNotNull(exchangeType, value -> manager.exchangeType = value)
228+
.acceptIfNotNull(declareExchange, value -> manager.declareExchange = value)
229+
.acceptIfNotNull(durable, value -> manager.durable = value)
230+
.acceptIfNotNull(autoDelete, value -> manager.autoDelete = value)
231+
.acceptIfNotNull(contentType, value -> manager.contentType = value)
232+
.acceptIfNotNull(contentEncoding, value -> manager.contentEncoding = value)
233+
.acceptIfNotNull(connectionName, value -> manager.connectionName = value)
234+
.acceptIfNotNull(clientConnectionProperties, value -> manager.clientConnectionProperties = value)
235+
.acceptIfNotNull(charset, value -> manager.charset = value)
236+
.acceptIfNotNull(async, value -> manager.async = value)
237+
.acceptIfNotNull(addMdcAsHeaders, value -> manager.addMdcAsHeaders = value);
233238

234239
BlockingQueue<Event> eventQueue;
235240
if (blockingQueueFactory == null) {
@@ -293,11 +298,10 @@ protected void sendEvent(Event event, Map<?, ?> properties) {
293298
Level level = logEvent.getLevel();
294299

295300
MessageProperties amqpProps = new MessageProperties();
296-
amqpProps.setDeliveryMode(this.manager.deliveryMode);
297-
amqpProps.setContentType(this.manager.contentType);
298-
if (null != this.manager.contentEncoding) {
299-
amqpProps.setContentEncoding(this.manager.contentEncoding);
300-
}
301+
JavaUtils.INSTANCE
302+
.acceptIfNotNull(this.manager.deliveryMode, amqpProps::setDeliveryMode)
303+
.acceptIfNotNull(this.manager.contentType, amqpProps::setContentType)
304+
.acceptIfNotNull(this.manager.contentEncoding, amqpProps::setContentEncoding);
301305
amqpProps.setHeader(CATEGORY_NAME, name);
302306
amqpProps.setHeader(THREAD_NAME, logEvent.getThreadName());
303307
amqpProps.setHeader(CATEGORY_LEVEL, level.toString());
@@ -578,6 +582,12 @@ protected static class AmqpManager extends AbstractManager {
578582
*/
579583
private String trustStoreType = "JKS";
580584

585+
/**
586+
* SaslConfig.
587+
* @see RabbitUtils#stringToSaslConfig(String, ConnectionFactory)
588+
*/
589+
public String saslConfig;
590+
581591
/**
582592
* Default content-type of log messages.
583593
*/
@@ -644,6 +654,7 @@ protected AmqpManager(LoggerContext loggerContext, String name) {
644654
private boolean activateOptions() {
645655
ConnectionFactory rabbitConnectionFactory = createRabbitConnectionFactory();
646656
if (rabbitConnectionFactory != null) {
657+
Assert.state(this.applicationId != null, "applicationId is required");
647658
this.routingKeyLayout = PatternLayout.newBuilder()
648659
.withPattern(this.routingKeyPattern.replaceAll("%X\\{applicationId}", this.applicationId))
649660
.withCharset(Charset.forName(this.charset))
@@ -721,6 +732,16 @@ protected void configureRabbitConnectionFactory(RabbitConnectionFactoryBean fact
721732
factoryBean.setTrustStore(this.trustStore);
722733
factoryBean.setTrustStorePassphrase(this.trustStorePassphrase);
723734
factoryBean.setTrustStoreType(this.trustStoreType);
735+
JavaUtils.INSTANCE
736+
.acceptIfNotNull(this.saslConfig, config -> {
737+
try {
738+
factoryBean.setSaslConfig(RabbitUtils.stringToSaslConfig(config,
739+
factoryBean.getRabbitConnectionFactory()));
740+
}
741+
catch (Exception e) {
742+
throw RabbitExceptionTranslator.convertRabbitAccessException(e);
743+
}
744+
});
724745
}
725746
}
726747
}

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/logback/AmqpAppender.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
4646
import org.springframework.amqp.rabbit.connection.ConnectionFactoryConfigurationUtils;
4747
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
48+
import org.springframework.amqp.rabbit.connection.RabbitUtils;
4849
import org.springframework.amqp.rabbit.core.DeclareExchangeConnectionListener;
4950
import org.springframework.amqp.rabbit.core.RabbitAdmin;
5051
import org.springframework.amqp.rabbit.core.RabbitTemplate;
52+
import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator;
5153
import org.springframework.amqp.utils.JavaUtils;
5254
import org.springframework.core.io.Resource;
5355
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@@ -62,6 +64,7 @@
6264
import ch.qos.logback.core.Layout;
6365
import ch.qos.logback.core.encoder.Encoder;
6466
import com.rabbitmq.client.ConnectionFactory;
67+
import com.rabbitmq.client.SaslConfig;
6568

6669
/**
6770
* A Logback appender that publishes logging events to an AMQP Exchange.
@@ -273,6 +276,12 @@ public class AmqpAppender extends AppenderBase<ILoggingEvent> {
273276
*/
274277
private String trustStoreType = "JKS";
275278

279+
/**
280+
* SaslConfig.
281+
* @see RabbitUtils#stringToSaslConfig(String, ConnectionFactory)
282+
*/
283+
public String saslConfig;
284+
276285
private boolean verifyHostname = true;
277286

278287
/**
@@ -472,6 +481,20 @@ public void setTrustStoreType(String trustStoreType) {
472481
this.trustStoreType = trustStoreType;
473482
}
474483

484+
public String getSaslConfig() {
485+
return this.saslConfig;
486+
}
487+
488+
/**
489+
* Set the {@link SaslConfig}.
490+
* @param saslConfig the saslConfig to set
491+
* @since 1.7.14
492+
* @see RabbitUtils#stringToSaslConfig(String, ConnectionFactory)
493+
*/
494+
public void setSaslConfig(String saslConfig) {
495+
this.saslConfig = saslConfig;
496+
}
497+
475498
public String getExchangeName() {
476499
return this.exchangeName;
477500
}
@@ -727,6 +750,16 @@ protected void configureRabbitConnectionFactory(RabbitConnectionFactoryBean fact
727750
factoryBean.setTrustStore(this.trustStore);
728751
factoryBean.setTrustStorePassphrase(this.trustStorePassphrase);
729752
factoryBean.setTrustStoreType(this.trustStoreType);
753+
JavaUtils.INSTANCE
754+
.acceptIfNotNull(this.saslConfig, config -> {
755+
try {
756+
factoryBean.setSaslConfig(RabbitUtils.stringToSaslConfig(config,
757+
factoryBean.getRabbitConnectionFactory()));
758+
}
759+
catch (Exception e) {
760+
throw RabbitExceptionTranslator.convertRabbitAccessException(e);
761+
}
762+
});
730763
}
731764
}
732765
if (this.layout == null && this.encoder == null) {

spring-rabbit/src/test/java/org/springframework/amqp/rabbit/log4j2/AmqpAppenderTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@
6161
import org.springframework.core.io.ClassPathResource;
6262
import org.springframework.test.util.ReflectionTestUtils;
6363

64+
import com.rabbitmq.client.ConnectionFactory;
65+
import com.rabbitmq.client.DefaultSaslConfig;
66+
import com.rabbitmq.client.JDKSaslConfig;
67+
import com.rabbitmq.client.impl.CRDemoMechanism;
68+
6469
/**
6570
* @author Gary Russell
6671
* @author Stephen Oakey
@@ -186,6 +191,33 @@ public void testAmqpAppenderEventQueueTypeDefaultsToLinkedBlockingQueue() throws
186191
assertThat(events, instanceOf(LinkedBlockingQueue.class));
187192
}
188193

194+
@Test
195+
public void testSaslConfig() {
196+
Logger logger = LogManager.getLogger("sasl");
197+
AmqpAppender appender = (AmqpAppender) TestUtils.getPropertyValue(logger, "context.configuration.appenders",
198+
Map.class).get("sasl1");
199+
assertThat(TestUtils.getPropertyValue(appender, "manager.connectionFactory.rabbitConnectionFactory",
200+
ConnectionFactory.class).getSaslConfig())
201+
.isInstanceOf(DefaultSaslConfig.class)
202+
.hasFieldOrPropertyWithValue("mechanism", "PLAIN");
203+
appender = (AmqpAppender) TestUtils.getPropertyValue(logger, "context.configuration.appenders",
204+
Map.class).get("sasl2");
205+
assertThat(TestUtils.getPropertyValue(appender, "manager.connectionFactory.rabbitConnectionFactory",
206+
ConnectionFactory.class).getSaslConfig())
207+
.isInstanceOf(DefaultSaslConfig.class)
208+
.hasFieldOrPropertyWithValue("mechanism", "EXTERNAL");
209+
appender = (AmqpAppender) TestUtils.getPropertyValue(logger, "context.configuration.appenders",
210+
Map.class).get("sasl3");
211+
assertThat(TestUtils.getPropertyValue(appender, "manager.connectionFactory.rabbitConnectionFactory",
212+
ConnectionFactory.class).getSaslConfig())
213+
.isInstanceOf(JDKSaslConfig.class);
214+
appender = (AmqpAppender) TestUtils.getPropertyValue(logger, "context.configuration.appenders",
215+
Map.class).get("sasl4");
216+
assertThat(TestUtils.getPropertyValue(appender, "manager.connectionFactory.rabbitConnectionFactory",
217+
ConnectionFactory.class).getSaslConfig())
218+
.isInstanceOf(CRDemoMechanism.CRDemoSaslConfig.class);
219+
}
220+
189221
@Test
190222
public void testUriProperties() {
191223
Logger logger = LogManager.getLogger("bar");

0 commit comments

Comments
 (0)