diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java index e1a750765c7..d63a21856a1 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.condition; +import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -90,53 +91,30 @@ class OnClassCondition extends SpringBootCondition // additional thread seems to offer the best performance. More threads make // things worse int split = autoConfigurationClasses.length / 2; - GetOutcomesThread thread = new GetOutcomesThread(autoConfigurationClasses, 0, - split, autoConfigurationMetadata); - thread.start(); - ConditionOutcome[] secondHalf = getOutcomes(autoConfigurationClasses, split, - autoConfigurationClasses.length, autoConfigurationMetadata); - try { - thread.join(); - } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - ConditionOutcome[] firstHalf = thread.getResult(); + OutcomesResolver firstHalfResolver = createOutcomesResolver( + autoConfigurationClasses, 0, split, autoConfigurationMetadata); + OutcomesResolver secondHalfResolver = new StandardOutcomesResolver( + autoConfigurationClasses, split, autoConfigurationClasses.length, + autoConfigurationMetadata, this.beanClassLoader); + ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); + ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length); System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length); return outcomes; } - private ConditionOutcome[] getOutcomes(final String[] autoConfigurationClasses, + private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { - ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; - for (int i = start; i < end; i++) { - String autoConfigurationClass = autoConfigurationClasses[i]; - Set candidates = autoConfigurationMetadata - .getSet(autoConfigurationClass, "ConditionalOnClass"); - if (candidates != null) { - outcomes[i - start] = getOutcome(candidates); - } - } - return outcomes; - } - - private ConditionOutcome getOutcome(Set candidates) { + OutcomesResolver outcomesResolver = new StandardOutcomesResolver( + autoConfigurationClasses, start, end, autoConfigurationMetadata, + this.beanClassLoader); try { - List missing = getMatches(candidates, MatchType.MISSING, - this.beanClassLoader); - if (!missing.isEmpty()) { - return ConditionOutcome - .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) - .didNotFind("required class", "required classes") - .items(Style.QUOTE, missing)); - } + return new ThreadedOutcomesResolver(outcomesResolver); } - catch (Exception ex) { - // We'll get another chance later + catch (AccessControlException ex) { + return outcomesResolver; } - return null; } @Override @@ -262,7 +240,45 @@ class OnClassCondition extends SpringBootCondition } - private class GetOutcomesThread extends Thread { + private interface OutcomesResolver { + + ConditionOutcome[] resolveOutcomes(); + + } + + private static final class ThreadedOutcomesResolver implements OutcomesResolver { + + private final Thread thread; + + private volatile ConditionOutcome[] outcomes; + + private ThreadedOutcomesResolver(final OutcomesResolver outcomesResolver) { + this.thread = new Thread(new Runnable() { + + @Override + public void run() { + ThreadedOutcomesResolver.this.outcomes = outcomesResolver + .resolveOutcomes(); + } + + }); + this.thread.start(); + } + + @Override + public ConditionOutcome[] resolveOutcomes() { + try { + this.thread.join(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + return this.outcomes; + } + + } + + private final class StandardOutcomesResolver implements OutcomesResolver { private final String[] autoConfigurationClasses; @@ -272,25 +288,55 @@ class OnClassCondition extends SpringBootCondition private final AutoConfigurationMetadata autoConfigurationMetadata; - private ConditionOutcome[] outcomes; + private final ClassLoader beanClassLoader; - GetOutcomesThread(String[] autoConfigurationClasses, int start, int end, - AutoConfigurationMetadata autoConfigurationMetadata) { + private StandardOutcomesResolver(String[] autoConfigurationClasses, int start, + int end, AutoConfigurationMetadata autoConfigurationMetadata, + ClassLoader beanClassLoader) { this.autoConfigurationClasses = autoConfigurationClasses; this.start = start; this.end = end; this.autoConfigurationMetadata = autoConfigurationMetadata; + this.beanClassLoader = beanClassLoader; } @Override - public void run() { - this.outcomes = getOutcomes(this.autoConfigurationClasses, this.start, - this.end, this.autoConfigurationMetadata); + public ConditionOutcome[] resolveOutcomes() { + return getOutcomes(this.autoConfigurationClasses, this.start, this.end, + this.autoConfigurationMetadata); } - public ConditionOutcome[] getResult() { - return this.outcomes; + private ConditionOutcome[] getOutcomes(final String[] autoConfigurationClasses, + int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { + ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; + for (int i = start; i < end; i++) { + String autoConfigurationClass = autoConfigurationClasses[i]; + Set candidates = autoConfigurationMetadata + .getSet(autoConfigurationClass, "ConditionalOnClass"); + if (candidates != null) { + outcomes[i - start] = getOutcome(candidates); + } + } + return outcomes; + } + + private ConditionOutcome getOutcome(Set candidates) { + try { + List missing = getMatches(candidates, MatchType.MISSING, + this.beanClassLoader); + if (!missing.isEmpty()) { + return ConditionOutcome.noMatch( + ConditionMessage.forCondition(ConditionalOnClass.class) + .didNotFind("required class", "required classes") + .items(Style.QUOTE, missing)); + } + } + catch (Exception ex) { + // We'll get another chance later + } + return null; } } + }