diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndi.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndi.java new file mode 100644 index 00000000000..8015c017496 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndi.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.condition; + +import javax.naming.InitialContext; + +import org.springframework.context.annotation.Conditional; + +/** + * {@link Conditional} that matches based on the availability of a JNDI + * {@link InitialContext} and the ability to lookup specific locations. + * + * @author Phillip Webb + * @since 1.2.0 + */ +@Conditional(OnJndiCondition.class) +public @interface ConditionalOnJndi { + + /** + * JNDI Locations, one of which must exist. If no locations are specific the condition + * matches solely based on the presence of an {@link InitialContext}. + */ + String[] value() default {}; + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java new file mode 100644 index 00000000000..3b85fb0dd1e --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJndiCondition.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.condition; + +import javax.naming.NamingException; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.jndi.JndiLocatorDelegate; +import org.springframework.jndi.JndiLocatorSupport; +import org.springframework.util.StringUtils; + +/** + * {@link Condition} that checks for JNDI locations. + * + * @author Phillip Webb + * @since 1.2.0 + * @see ConditionalOnJndi + */ +class OnJndiCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata + .getAnnotationAttributes(ConditionalOnJndi.class.getName())); + String[] locations = annotationAttributes.getStringArray("value"); + try { + return getMatchOutcome(locations); + } + catch (NoClassDefFoundError ex) { + return ConditionOutcome.noMatch("JNDI class not found"); + } + } + + private ConditionOutcome getMatchOutcome(String[] locations) { + if (!isJndiAvailable()) { + return ConditionOutcome.noMatch("JNDI environment is not available"); + } + if (locations.length == 0) { + return ConditionOutcome.match("JNDI environment is available"); + } + JndiLocator locator = getJndiLocator(locations); + String location = locator.lookupFirstLocation(); + if (location != null) { + return ConditionOutcome.match("JNDI location '" + location + + "' found from candidates " + + StringUtils.arrayToCommaDelimitedString(locations)); + } + return ConditionOutcome.noMatch("No JNDI location found from candidates " + + StringUtils.arrayToCommaDelimitedString(locations)); + } + + protected boolean isJndiAvailable() { + return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable(); + } + + protected JndiLocator getJndiLocator(String[] locations) { + return new JndiLocator(locations); + } + + protected static class JndiLocator extends JndiLocatorSupport { + + private String[] locations; + + public JndiLocator(String[] locations) { + this.locations = locations; + } + + public String lookupFirstLocation() { + for (String location : this.locations) { + try { + lookup(location); + return location; + } + catch (NamingException ex) { + // Swallow and continue + } + } + return null; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java index 92cc4599f29..2feb63e04ca 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java @@ -20,6 +20,7 @@ import javax.jms.ConnectionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,6 +30,7 @@ import org.springframework.jms.annotation.JmsBootstrapConfiguration; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerConfigUtils; import org.springframework.jms.support.destination.DestinationResolver; +import org.springframework.jms.support.destination.JndiDestinationResolver; import org.springframework.transaction.PlatformTransactionManager; /** @@ -67,6 +69,18 @@ class JmsAnnotationDrivenConfiguration { @EnableJms @ConditionalOnMissingBean(name = JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME) protected static class EnableJmsConfiguration { + } + + @ConditionalOnJndi + protected static class JndiConfiguration { + + @Bean + @ConditionalOnMissingBean + public DestinationResolver destinationResolver() { + JndiDestinationResolver resolver = new JndiDestinationResolver(); + resolver.setFallbackToDynamicDestination(true); + return resolver; + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JndiConnectionFactoryAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JndiConnectionFactoryAutoConfiguration.java new file mode 100644 index 00000000000..9bb79bc3898 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JndiConnectionFactoryAutoConfiguration.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jms; + +import javax.jms.ConnectionFactory; +import javax.naming.NamingException; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jndi.JndiLocatorDelegate; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for JMS provided from JNDI. + * + * @author Phillip Webb + * @since 1.2.0 + */ +@Configuration +@AutoConfigureBefore(JmsAutoConfiguration.class) +@ConditionalOnMissingBean(ConnectionFactory.class) +@ConditionalOnJndi("java:/JmsXA") +public class JndiConnectionFactoryAutoConfiguration { + + @Bean + public ConnectionFactory connectionFactory() throws NamingException { + return new JndiLocatorDelegate().lookup("java:/JmsXA", ConnectionFactory.class); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JndiJtaConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JndiJtaConfiguration.java new file mode 100644 index 00000000000..92f7558942e --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JndiJtaConfiguration.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jta; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.jta.JtaTransactionManager; + +/** + * JTA Configuration for a JDNI managed {@link JtaTransactionManager}. + * + * @author Phillip Webb + * @since 1.2.0 + */ +@Configuration +@ConditionalOnJndi({ JtaTransactionManager.DEFAULT_USER_TRANSACTION_NAME, + "java:comp/TransactionManager", "java:appserver/TransactionManager", + "java:pm/TransactionManager", "java:/TransactionManager" }) +@ConditionalOnMissingBean(PlatformTransactionManager.class) +class JndiJtaConfiguration { + + @Bean + public JtaTransactionManager transactionManager() { + return new JtaTransactionManager(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfiguration.java index 803d4c55e2b..4a0742d1bc8 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jta/JtaAutoConfiguration.java @@ -29,7 +29,8 @@ import org.springframework.context.annotation.Import; * @since 1.2.0 */ @ConditionalOnClass(javax.transaction.Transaction.class) -@Import({ BitronixJtaConfiguration.class, AtomikosJtaConfiguration.class }) +@Import({ JndiJtaConfiguration.class, BitronixJtaConfiguration.class, + AtomikosJtaConfiguration.class }) @EnableConfigurationProperties(JtaProperties.class) public class JtaAutoConfiguration { diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 8a3f4a6458f..8fe67068012 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -24,6 +24,7 @@ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ +org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\ org.springframework.boot.autoconfigure.jta.JtaAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionOnJndiTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionOnJndiTests.java new file mode 100644 index 00000000000..5d0f9aec29b --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionOnJndiTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.condition; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.springframework.core.type.AnnotatedTypeMetadata; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link OnJndiCondition}. + * + * @author Phillip Webb + */ +public class ConditionOnJndiTests { + + private MockableOnJndi condition = new MockableOnJndi(); + + @Test + public void jndiNotAvailable() { + this.condition.setJndiAvailable(false); + ConditionOutcome outcome = this.condition.getMatchOutcome(null, mockMetaData()); + assertThat(outcome.isMatch(), equalTo(false)); + } + + @Test + public void jndiLocationNotFound() { + ConditionOutcome outcome = this.condition.getMatchOutcome(null, + mockMetaData("java:/a")); + assertThat(outcome.isMatch(), equalTo(false)); + } + + @Test + public void jndiLocationFound() { + this.condition.setFoundLocation("java:/b"); + ConditionOutcome outcome = this.condition.getMatchOutcome(null, + mockMetaData("java:/a", "java:/b")); + assertThat(outcome.isMatch(), equalTo(true)); + } + + private AnnotatedTypeMetadata mockMetaData(String... value) { + AnnotatedTypeMetadata metadata = mock(AnnotatedTypeMetadata.class); + Map attributes = new HashMap(); + attributes.put("value", value); + given(metadata.getAnnotationAttributes(ConditionalOnJndi.class.getName())) + .willReturn(attributes); + return metadata; + } + + private static class MockableOnJndi extends OnJndiCondition { + + private boolean jndiAvailable = true; + + private String foundLocation; + + @Override + protected boolean isJndiAvailable() { + return this.jndiAvailable; + } + + @Override + protected JndiLocator getJndiLocator(String[] locations) { + return new JndiLocator(locations) { + @Override + public String lookupFirstLocation() { + return MockableOnJndi.this.foundLocation; + } + }; + } + + public void setJndiAvailable(boolean jndiAvailable) { + this.jndiAvailable = jndiAvailable; + } + + public void setFoundLocation(String foundLocation) { + this.foundLocation = foundLocation; + } + } + +}