Skip to content

Commit 239d19d

Browse files
committed
Support JNDI for JTA and JMS.
Update auto-configuration for JMS and JTA to support a ConnectionFactory and TransactionManager exposed via JNDI. JTA configuration now attempts a simple JtaTransactionManager before attempting Bitronix or Atomikos configuration. The JMS auto-configuration also now attempts to find a ConnectionFactory from JNDI before falling back to the previous strategies. If JNDI is present a JndiDestinationResolver is also configured instead of the default DestinationResolver. See spring-projectsgh-947
1 parent c15e3a7 commit 239d19d

File tree

8 files changed

+348
-1
lines changed

8 files changed

+348
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-2014 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+
* http://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.condition;
18+
19+
import javax.naming.InitialContext;
20+
21+
import org.springframework.context.annotation.Conditional;
22+
23+
/**
24+
* {@link Conditional} that matches based on the availability of a JNDI
25+
* {@link InitialContext} and the ability to lookup specific locations.
26+
*
27+
* @author Phillip Webb
28+
* @since 1.2.0
29+
*/
30+
@Conditional(OnJndiCondition.class)
31+
public @interface ConditionalOnJndi {
32+
33+
/**
34+
* JNDI Locations, one of which must exist. If no locations are specific the condition
35+
* matches solely based on the presence of an {@link InitialContext}.
36+
*/
37+
String[] value() default {};
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2012-2014 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+
* http://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.condition;
18+
19+
import javax.naming.NamingException;
20+
21+
import org.springframework.context.annotation.Condition;
22+
import org.springframework.context.annotation.ConditionContext;
23+
import org.springframework.core.annotation.AnnotationAttributes;
24+
import org.springframework.core.type.AnnotatedTypeMetadata;
25+
import org.springframework.jndi.JndiLocatorDelegate;
26+
import org.springframework.jndi.JndiLocatorSupport;
27+
import org.springframework.util.StringUtils;
28+
29+
/**
30+
* {@link Condition} that checks for JNDI locations.
31+
*
32+
* @author Phillip Webb
33+
* @since 1.2.0
34+
* @see ConditionalOnJndi
35+
*/
36+
class OnJndiCondition extends SpringBootCondition {
37+
38+
@Override
39+
public ConditionOutcome getMatchOutcome(ConditionContext context,
40+
AnnotatedTypeMetadata metadata) {
41+
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata
42+
.getAnnotationAttributes(ConditionalOnJndi.class.getName()));
43+
String[] locations = annotationAttributes.getStringArray("value");
44+
try {
45+
return getMatchOutcome(locations);
46+
}
47+
catch (NoClassDefFoundError ex) {
48+
return ConditionOutcome.noMatch("JNDI class not found");
49+
}
50+
}
51+
52+
private ConditionOutcome getMatchOutcome(String[] locations) {
53+
if (!isJndiAvailable()) {
54+
return ConditionOutcome.noMatch("JNDI environment is not available");
55+
}
56+
if (locations.length == 0) {
57+
return ConditionOutcome.match("JNDI environment is available");
58+
}
59+
JndiLocator locator = getJndiLocator(locations);
60+
String location = locator.lookupFirstLocation();
61+
if (location != null) {
62+
return ConditionOutcome.match("JNDI location '" + location
63+
+ "' found from candidates "
64+
+ StringUtils.arrayToCommaDelimitedString(locations));
65+
}
66+
return ConditionOutcome.noMatch("No JNDI location found from candidates "
67+
+ StringUtils.arrayToCommaDelimitedString(locations));
68+
}
69+
70+
protected boolean isJndiAvailable() {
71+
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
72+
}
73+
74+
protected JndiLocator getJndiLocator(String[] locations) {
75+
return new JndiLocator(locations);
76+
}
77+
78+
protected static class JndiLocator extends JndiLocatorSupport {
79+
80+
private String[] locations;
81+
82+
public JndiLocator(String[] locations) {
83+
this.locations = locations;
84+
}
85+
86+
public String lookupFirstLocation() {
87+
for (String location : this.locations) {
88+
try {
89+
lookup(location);
90+
return location;
91+
}
92+
catch (NamingException ex) {
93+
// Swallow and continue
94+
}
95+
}
96+
return null;
97+
}
98+
99+
}
100+
101+
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java

+14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.beans.factory.annotation.Autowired;
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2425
import org.springframework.context.annotation.Bean;
2526
import org.springframework.context.annotation.Configuration;
@@ -29,6 +30,7 @@
2930
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
3031
import org.springframework.jms.config.JmsListenerConfigUtils;
3132
import org.springframework.jms.support.destination.DestinationResolver;
33+
import org.springframework.jms.support.destination.JndiDestinationResolver;
3234
import org.springframework.transaction.PlatformTransactionManager;
3335

3436
/**
@@ -67,6 +69,18 @@ public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
6769
@EnableJms
6870
@ConditionalOnMissingBean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
6971
protected static class EnableJmsConfiguration {
72+
}
73+
74+
@ConditionalOnJndi
75+
protected static class JndiConfiguration {
76+
77+
@Bean
78+
@ConditionalOnMissingBean
79+
public DestinationResolver destinationResolver() {
80+
JndiDestinationResolver resolver = new JndiDestinationResolver();
81+
resolver.setFallbackToDynamicDestination(true);
82+
return resolver;
83+
}
7084

7185
}
7286

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2014 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+
* http://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;
18+
19+
import javax.jms.ConnectionFactory;
20+
import javax.naming.NamingException;
21+
22+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
23+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
25+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.jndi.JndiLocatorDelegate;
29+
30+
/**
31+
* {@link EnableAutoConfiguration Auto-configuration} for JMS provided from JNDI.
32+
*
33+
* @author Phillip Webb
34+
* @since 1.2.0
35+
*/
36+
@Configuration
37+
@AutoConfigureBefore(JmsAutoConfiguration.class)
38+
@ConditionalOnMissingBean(ConnectionFactory.class)
39+
@ConditionalOnJndi("java:/JmsXA")
40+
public class JndiConnectionFactoryAutoConfiguration {
41+
42+
@Bean
43+
public ConnectionFactory connectionFactory() throws NamingException {
44+
return new JndiLocatorDelegate().lookup("java:/JmsXA", ConnectionFactory.class);
45+
}
46+
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2014 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+
* http://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.jta;
18+
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
20+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.transaction.PlatformTransactionManager;
24+
import org.springframework.transaction.jta.JtaTransactionManager;
25+
26+
/**
27+
* JTA Configuration for a JDNI managed {@link JtaTransactionManager}.
28+
*
29+
* @author Phillip Webb
30+
* @since 1.2.0
31+
*/
32+
@Configuration
33+
@ConditionalOnJndi({ JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME,
34+
"java:comp/TransactionManager", "java:appserver/TransactionManager",
35+
"java:pm/TransactionManager", "java:/TransactionManager" })
36+
@ConditionalOnMissingBean(PlatformTransactionManager.class)
37+
class JndiJtaConfiguration {
38+
39+
@Bean
40+
public JtaTransactionManager transactionManager() {
41+
return new JtaTransactionManager();
42+
}
43+
44+
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfiguration.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
* @since 1.2.0
3030
*/
3131
@ConditionalOnClass(javax.transaction.Transaction.class)
32-
@Import({ BitronixJtaConfiguration.class, AtomikosJtaConfiguration.class })
32+
@Import({ JndiJtaConfiguration.class, BitronixJtaConfiguration.class,
33+
AtomikosJtaConfiguration.class })
3334
@EnableConfigurationProperties(JtaProperties.class)
3435
public class JtaAutoConfiguration {
3536

spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
2424
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
2525
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
2626
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
27+
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
2728
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
2829
org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\
2930
org.springframework.boot.autoconfigure.jta.JtaAutoConfiguration,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2012-2014 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+
* http://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.condition;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.junit.Test;
23+
import org.springframework.core.type.AnnotatedTypeMetadata;
24+
25+
import static org.hamcrest.Matchers.equalTo;
26+
import static org.junit.Assert.assertThat;
27+
import static org.mockito.BDDMockito.given;
28+
import static org.mockito.Mockito.mock;
29+
30+
/**
31+
* Tests for {@link OnJndiCondition}.
32+
*
33+
* @author Phillip Webb
34+
*/
35+
public class ConditionOnJndiTests {
36+
37+
private MockableOnJndi condition = new MockableOnJndi();
38+
39+
@Test
40+
public void jndiNotAvailable() {
41+
this.condition.setJndiAvailable(false);
42+
ConditionOutcome outcome = this.condition.getMatchOutcome(null, mockMetaData());
43+
assertThat(outcome.isMatch(), equalTo(false));
44+
}
45+
46+
@Test
47+
public void jndiLocationNotFound() {
48+
ConditionOutcome outcome = this.condition.getMatchOutcome(null,
49+
mockMetaData("java:/a"));
50+
assertThat(outcome.isMatch(), equalTo(false));
51+
}
52+
53+
@Test
54+
public void jndiLocationFound() {
55+
this.condition.setFoundLocation("java:/b");
56+
ConditionOutcome outcome = this.condition.getMatchOutcome(null,
57+
mockMetaData("java:/a", "java:/b"));
58+
assertThat(outcome.isMatch(), equalTo(true));
59+
}
60+
61+
private AnnotatedTypeMetadata mockMetaData(String... value) {
62+
AnnotatedTypeMetadata metadata = mock(AnnotatedTypeMetadata.class);
63+
Map<String, Object> attributes = new HashMap<String, Object>();
64+
attributes.put("value", value);
65+
given(metadata.getAnnotationAttributes(ConditionalOnJndi.class.getName()))
66+
.willReturn(attributes);
67+
return metadata;
68+
}
69+
70+
private static class MockableOnJndi extends OnJndiCondition {
71+
72+
private boolean jndiAvailable = true;
73+
74+
private String foundLocation;
75+
76+
@Override
77+
protected boolean isJndiAvailable() {
78+
return this.jndiAvailable;
79+
}
80+
81+
@Override
82+
protected JndiLocator getJndiLocator(String[] locations) {
83+
return new JndiLocator(locations) {
84+
@Override
85+
public String lookupFirstLocation() {
86+
return MockableOnJndi.this.foundLocation;
87+
}
88+
};
89+
}
90+
91+
public void setJndiAvailable(boolean jndiAvailable) {
92+
this.jndiAvailable = jndiAvailable;
93+
}
94+
95+
public void setFoundLocation(String foundLocation) {
96+
this.foundLocation = foundLocation;
97+
}
98+
}
99+
100+
}

0 commit comments

Comments
 (0)