Add @ConditionalOnJava

Added a new @ConditionalOnJava annotation that allows to conditionally
enable configuration based on the Java version that is running. 

The annotation currently supports two modes of restricting Java versions:
the default mode checks for a Java version equal or better than the
requested one. Beyond that it can be configured to only match if Java
version is older than the configured one.
This commit is contained in:
Oliver Gierke 2014-06-06 12:04:39 +02:00 committed by Andy Wilkinson
parent e0a5c29601
commit 30bef1e95e
3 changed files with 285 additions and 0 deletions

View File

@ -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;
}
}
}

View File

@ -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<String, Object> 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;
}
}
}

View File

@ -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<Iterable<String>> matcher = iterableWithSize(expectedNumber);
assertThat(this.context.getBeansOfType(String.class).values(), is(matcher));
}
}