Skip to content

Commit 60eb01b

Browse files
committed
Merge pull request #35048 from bendiscz
* pr/35048: Polish "Reintroduce support for ActiveMQ" Reintroduce support for ActiveMQ Closes gh-35048
2 parents bed1167 + a323bd9 commit 60eb01b

File tree

25 files changed

+1237
-5
lines changed

25 files changed

+1237
-5
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ dependencies {
7777
optional("jakarta.persistence:jakarta.persistence-api")
7878
optional("jakarta.servlet:jakarta.servlet-api")
7979
optional("javax.cache:cache-api")
80+
optional("org.apache.activemq:activemq-client-jakarta")
8081
optional("org.apache.commons:commons-dbcp2") {
8182
exclude group: "commons-logging", module: "commons-logging"
8283
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/jms/JmsHealthContributorAutoConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3030
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3131
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
32+
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
3233
import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration;
3334
import org.springframework.context.annotation.Bean;
3435

@@ -38,7 +39,7 @@
3839
* @author Stephane Nicoll
3940
* @since 2.0.0
4041
*/
41-
@AutoConfiguration(after = ArtemisAutoConfiguration.class)
42+
@AutoConfiguration(after = { ActiveMQAutoConfiguration.class, ArtemisAutoConfiguration.class })
4243
@ConditionalOnClass(ConnectionFactory.class)
4344
@ConditionalOnBean(ConnectionFactory.class)
4445
@ConditionalOnEnabledHealthIndicator("jms")

spring-boot-project/spring-boot-autoconfigure/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ dependencies {
4848
optional("jakarta.ws.rs:jakarta.ws.rs-api")
4949
optional("javax.cache:cache-api")
5050
optional("javax.money:money-api")
51+
optional("org.apache.activemq:activemq-client-jakarta")
5152
optional("org.apache.activemq:artemis-jakarta-client") {
5253
exclude group: "commons-logging", module: "commons-logging"
5354
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jms.activemq;
18+
19+
import jakarta.jms.ConnectionFactory;
20+
import org.apache.activemq.ActiveMQConnectionFactory;
21+
22+
import org.springframework.boot.autoconfigure.AutoConfiguration;
23+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
27+
import org.springframework.boot.autoconfigure.jms.JmsProperties;
28+
import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration;
29+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
30+
import org.springframework.context.annotation.Import;
31+
32+
/**
33+
* {@link EnableAutoConfiguration Auto-configuration} to integrate with an ActiveMQ
34+
* broker.
35+
*
36+
* @author Stephane Nicoll
37+
* @author Phillip Webb
38+
* @since 3.1.0
39+
*/
40+
@AutoConfiguration(before = JmsAutoConfiguration.class, after = JndiConnectionFactoryAutoConfiguration.class)
41+
@ConditionalOnClass({ ConnectionFactory.class, ActiveMQConnectionFactory.class })
42+
@ConditionalOnMissingBean(ConnectionFactory.class)
43+
@EnableConfigurationProperties({ ActiveMQProperties.class, JmsProperties.class })
44+
@Import({ ActiveMQXAConnectionFactoryConfiguration.class, ActiveMQConnectionFactoryConfiguration.class })
45+
public class ActiveMQAutoConfiguration {
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jms.activemq;
18+
19+
import java.util.stream.Collectors;
20+
21+
import jakarta.jms.ConnectionFactory;
22+
import org.apache.activemq.ActiveMQConnectionFactory;
23+
import org.apache.commons.pool2.PooledObject;
24+
import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;
25+
26+
import org.springframework.beans.factory.ObjectProvider;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
30+
import org.springframework.boot.autoconfigure.jms.JmsPoolConnectionFactoryFactory;
31+
import org.springframework.boot.autoconfigure.jms.JmsProperties;
32+
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.annotation.Configuration;
34+
import org.springframework.jms.connection.CachingConnectionFactory;
35+
36+
/**
37+
* Configuration for ActiveMQ {@link ConnectionFactory}.
38+
*
39+
* @author Greg Turnquist
40+
* @author Stephane Nicoll
41+
* @author Phillip Webb
42+
* @author Andy Wilkinson
43+
* @author Aurélien Leboulanger
44+
*/
45+
@Configuration(proxyBeanMethods = false)
46+
@ConditionalOnMissingBean(ConnectionFactory.class)
47+
class ActiveMQConnectionFactoryConfiguration {
48+
49+
@Configuration(proxyBeanMethods = false)
50+
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false",
51+
matchIfMissing = true)
52+
static class SimpleConnectionFactoryConfiguration {
53+
54+
@Bean
55+
@ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false")
56+
ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
57+
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
58+
return createJmsConnectionFactory(properties, factoryCustomizers);
59+
}
60+
61+
private static ActiveMQConnectionFactory createJmsConnectionFactory(ActiveMQProperties properties,
62+
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
63+
return new ActiveMQConnectionFactoryFactory(properties,
64+
factoryCustomizers.orderedStream().collect(Collectors.toList()))
65+
.createConnectionFactory(ActiveMQConnectionFactory.class);
66+
}
67+
68+
@Configuration(proxyBeanMethods = false)
69+
@ConditionalOnClass(CachingConnectionFactory.class)
70+
@ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "true",
71+
matchIfMissing = true)
72+
static class CachingConnectionFactoryConfiguration {
73+
74+
@Bean
75+
CachingConnectionFactory jmsConnectionFactory(JmsProperties jmsProperties, ActiveMQProperties properties,
76+
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
77+
JmsProperties.Cache cacheProperties = jmsProperties.getCache();
78+
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
79+
createJmsConnectionFactory(properties, factoryCustomizers));
80+
connectionFactory.setCacheConsumers(cacheProperties.isConsumers());
81+
connectionFactory.setCacheProducers(cacheProperties.isProducers());
82+
connectionFactory.setSessionCacheSize(cacheProperties.getSessionCacheSize());
83+
return connectionFactory;
84+
}
85+
86+
}
87+
88+
}
89+
90+
@Configuration(proxyBeanMethods = false)
91+
@ConditionalOnClass({ JmsPoolConnectionFactory.class, PooledObject.class })
92+
static class PooledConnectionFactoryConfiguration {
93+
94+
@Bean(destroyMethod = "stop")
95+
@ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true")
96+
JmsPoolConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
97+
ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
98+
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(properties,
99+
factoryCustomizers.orderedStream().collect(Collectors.toList()))
100+
.createConnectionFactory(ActiveMQConnectionFactory.class);
101+
return new JmsPoolConnectionFactoryFactory(properties.getPool())
102+
.createPooledConnectionFactory(connectionFactory);
103+
}
104+
105+
}
106+
107+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jms.activemq;
18+
19+
import org.apache.activemq.ActiveMQConnectionFactory;
20+
21+
/**
22+
* Callback interface that can be implemented by beans wishing to customize the
23+
* {@link ActiveMQConnectionFactory} whilst retaining default auto-configuration.
24+
*
25+
* @author Stephane Nicoll
26+
* @since 3.1.0
27+
*/
28+
@FunctionalInterface
29+
public interface ActiveMQConnectionFactoryCustomizer {
30+
31+
/**
32+
* Customize the {@link ActiveMQConnectionFactory}.
33+
* @param factory the factory to customize
34+
*/
35+
void customize(ActiveMQConnectionFactory factory);
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jms.activemq;
18+
19+
import java.lang.reflect.InvocationTargetException;
20+
import java.util.Collections;
21+
import java.util.List;
22+
23+
import org.apache.activemq.ActiveMQConnectionFactory;
24+
25+
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties.Packages;
26+
import org.springframework.util.Assert;
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* Factory to create a {@link ActiveMQConnectionFactory} instance from properties defined
31+
* in {@link ActiveMQProperties}.
32+
*
33+
* @author Phillip Webb
34+
* @author Venil Noronha
35+
*/
36+
class ActiveMQConnectionFactoryFactory {
37+
38+
private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
39+
40+
private final ActiveMQProperties properties;
41+
42+
private final List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers;
43+
44+
ActiveMQConnectionFactoryFactory(ActiveMQProperties properties,
45+
List<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
46+
Assert.notNull(properties, "Properties must not be null");
47+
this.properties = properties;
48+
this.factoryCustomizers = (factoryCustomizers != null) ? factoryCustomizers : Collections.emptyList();
49+
}
50+
51+
<T extends ActiveMQConnectionFactory> T createConnectionFactory(Class<T> factoryClass) {
52+
try {
53+
return doCreateConnectionFactory(factoryClass);
54+
}
55+
catch (Exception ex) {
56+
throw new IllegalStateException("Unable to create ActiveMQConnectionFactory", ex);
57+
}
58+
}
59+
60+
private <T extends ActiveMQConnectionFactory> T doCreateConnectionFactory(Class<T> factoryClass) throws Exception {
61+
T factory = createConnectionFactoryInstance(factoryClass);
62+
if (this.properties.getCloseTimeout() != null) {
63+
factory.setCloseTimeout((int) this.properties.getCloseTimeout().toMillis());
64+
}
65+
factory.setNonBlockingRedelivery(this.properties.isNonBlockingRedelivery());
66+
if (this.properties.getSendTimeout() != null) {
67+
factory.setSendTimeout((int) this.properties.getSendTimeout().toMillis());
68+
}
69+
Packages packages = this.properties.getPackages();
70+
if (packages.getTrustAll() != null) {
71+
factory.setTrustAllPackages(packages.getTrustAll());
72+
}
73+
if (!packages.getTrusted().isEmpty()) {
74+
factory.setTrustedPackages(packages.getTrusted());
75+
}
76+
customize(factory);
77+
return factory;
78+
}
79+
80+
private <T extends ActiveMQConnectionFactory> T createConnectionFactoryInstance(Class<T> factoryClass)
81+
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
82+
String brokerUrl = determineBrokerUrl();
83+
String user = this.properties.getUser();
84+
String password = this.properties.getPassword();
85+
if (StringUtils.hasLength(user) && StringUtils.hasLength(password)) {
86+
return factoryClass.getConstructor(String.class, String.class, String.class)
87+
.newInstance(user, password, brokerUrl);
88+
}
89+
return factoryClass.getConstructor(String.class).newInstance(brokerUrl);
90+
}
91+
92+
private void customize(ActiveMQConnectionFactory connectionFactory) {
93+
for (ActiveMQConnectionFactoryCustomizer factoryCustomizer : this.factoryCustomizers) {
94+
factoryCustomizer.customize(connectionFactory);
95+
}
96+
}
97+
98+
String determineBrokerUrl() {
99+
if (this.properties.getBrokerUrl() != null) {
100+
return this.properties.getBrokerUrl();
101+
}
102+
return DEFAULT_NETWORK_BROKER_URL;
103+
}
104+
105+
}

0 commit comments

Comments
 (0)