diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJava.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJava.java new file mode 100644 index 00000000000..141b7ad4257 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJava.java @@ -0,0 +1,116 @@ +/* + * 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.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; +import org.springframework.util.Assert; + +/** + * {@link Conditional} that matches based on the JVM version the application is running + * on. + * + * @author Oliver Gierke + * @since 1.1.0 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Conditional(OnJavaCondition.class) +public @interface ConditionalOnJava { + + /** + * Configures whether the value configured in {@link #value()} shall be considered the + * upper exclusive or lower inclusive boundary. Defaults to + * {@link Range#EQUAL_OR_NEWER}. + * @return the range of the version + */ + Range range() default Range.EQUAL_OR_NEWER; + + /** + * The {@link JavaVersion} to check for. Use {@link #range()} to specify whether the + * configured value is an upper-exclusive or lower-inclusive boundary. + */ + JavaVersion value(); + + public enum Range { + + OLDER_THAN("older than %s"), EQUAL_OR_NEWER("%s or newer"); + + private final String message; + + private Range(String message) { + this.message = message; + } + + public String getMessage(JavaVersion version) { + return String.format(this.message, version); + } + } + + /** + * An enum to abstract major Java versions. + */ + public enum JavaVersion { + + FIVE("1.5"), SIX("1.6"), SEVEN("1.7"), EIGHT("1.8"), NINE("1.9"); + + private String value; + + private JavaVersion(String value) { + this.value = value; + } + + /** + * Returns the {@link JavaVersion} of the current runtime. + */ + public static JavaVersion fromRuntime() { + + String source = System.getProperty("java.version"); + + for (JavaVersion version : JavaVersion.values()) { + if (source.startsWith(version.value)) { + return version; + } + } + + throw new IllegalArgumentException(String.format( + "Could not detect Java version for %s.", source)); + } + + /** + * Returns whether the given {@link JavaVersion} is considered equal or better + * than the given one. + * + * @param version must not be {code null}. + */ + public boolean isEqualOrBetter(JavaVersion version) { + + Assert.notNull(version, "Java version must not be null!"); + return this.value.compareTo(version.value) >= 0; + } + + @Override + public String toString() { + return this.value; + } + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java new file mode 100644 index 00000000000..c0e0e77c3c9 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnJavaCondition.java @@ -0,0 +1,65 @@ +/* + * 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.Map; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * {@link Condition} that checks for a required version of Java + * + * @author Oliver Gierke + * @see ConditionalOnJava + * @since 1.1.0 + */ +class OnJavaCondition extends SpringBootCondition { + + private static final JavaVersion JVM_VERSION = JavaVersion.fromRuntime(); + private static final String MATCH_MESSAGE = "Required JVM version %s and found %s."; + private static final String NO_MATCH_MESSAGE = "Required JVM version %s but found %s."; + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + + Map attributes = metadata + .getAnnotationAttributes(ConditionalOnJava.class.getName()); + + JavaVersion version = (JavaVersion) attributes.get("value"); + Range range = (Range) attributes.get("range"); + + ConditionOutcome match = ConditionOutcome.match(// + String.format(MATCH_MESSAGE, range.getMessage(version), JVM_VERSION)); + ConditionOutcome noMatch = ConditionOutcome.noMatch(// + String.format(NO_MATCH_MESSAGE, range.getMessage(version), JVM_VERSION)); + + boolean equalOrBetter = JVM_VERSION.isEqualOrBetter(version); + + switch (range) { + case OLDER_THAN: + return equalOrBetter ? noMatch : match; + case EQUAL_OR_NEWER: + default: + return equalOrBetter ? match : noMatch; + } + } +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJavaTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJavaTests.java new file mode 100644 index 00000000000..132077f1418 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJavaTests.java @@ -0,0 +1,104 @@ +/* + * 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 org.hamcrest.Matcher; +import org.junit.Test; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.iterableWithSize; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link ConditionalOnJava}. + * + * @author Oliver Gierke + */ +public class ConditionalOnJavaTests { + + private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @Test + public void doesNotMatchIfBetterVersionIsRequired() { + + this.context.register(Java9Required.class); + this.context.refresh(); + + assertPresent(false); + } + + @Test + public void doesNotMatchIfLowerIsRequired() { + + this.context.register(Java5Required.class); + this.context.refresh(); + + assertPresent(false); + } + + @Test + public void matchesIfVersionIsInRange() { + + this.context.register(Java6Required.class); + this.context.refresh(); + + assertPresent(true); + } + + @Configuration + @ConditionalOnJava(JavaVersion.NINE) + static class Java9Required { + + @Bean + String foo() { + return "foo"; + } + } + + @Configuration + @ConditionalOnJava(value = JavaVersion.SIX, range = Range.OLDER_THAN) + static class Java5Required { + + @Bean + String foo() { + return "foo"; + } + } + + @Configuration + @ConditionalOnJava(JavaVersion.SIX) + static class Java6Required { + + @Bean + String foo() { + return "foo"; + } + } + + private void assertPresent(boolean expected) { + + int expectedNumber = expected ? 1 : 0; + Matcher> matcher = iterableWithSize(expectedNumber); + + assertThat(this.context.getBeansOfType(String.class).values(), is(matcher)); + } +}